blob: 60e695b7b69aacc63c3679b9b045968136d40d58 [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Jouni Malinen026331c2010-02-15 12:53:10 +02004 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090010#include <linux/slab.h>
Johannes Berg55682962007-09-20 13:09:35 -040011#include <linux/list.h>
12#include <linux/if_ether.h>
13#include <linux/ieee80211.h>
14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h>
16#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010017#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020018#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040019#include <net/genetlink.h>
20#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020021#include <net/sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040022#include "core.h"
23#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070024#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040025
Jouni Malinen5fb628e2011-08-10 23:54:35 +030026static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type);
27static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
28 struct genl_info *info,
29 struct cfg80211_crypto_settings *settings,
30 int cipher_limit);
31
Johannes Berg4c476992010-10-04 21:36:35 +020032static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
34static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
35 struct genl_info *info);
36
Johannes Berg55682962007-09-20 13:09:35 -040037/* the netlink family */
38static struct genl_family nl80211_fam = {
39 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
40 .name = "nl80211", /* have users key off the name instead */
41 .hdrsize = 0, /* no private header */
42 .version = 1, /* no particular meaning now */
43 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020044 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020045 .pre_doit = nl80211_pre_doit,
46 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040047};
48
Johannes Berg79c97e92009-07-07 03:56:12 +020049/* internal helper: get rdev and dev */
Johannes Berg00918d32011-12-13 17:22:05 +010050static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs,
51 struct cfg80211_registered_device **rdev,
52 struct net_device **dev)
Johannes Berg55682962007-09-20 13:09:35 -040053{
54 int ifindex;
55
Johannes Bergbba95fe2008-07-29 13:22:51 +020056 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040057 return -EINVAL;
58
Johannes Bergbba95fe2008-07-29 13:22:51 +020059 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg00918d32011-12-13 17:22:05 +010060 *dev = dev_get_by_index(netns, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040061 if (!*dev)
62 return -ENODEV;
63
Johannes Berg00918d32011-12-13 17:22:05 +010064 *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020065 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040066 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020067 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040068 }
69
70 return 0;
71}
72
73/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000074static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040075 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
76 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070077 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020078 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020079 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053080 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020081 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
82 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
83 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
84 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010085 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040086
87 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
88 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
89 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010090
Eliad Pellere007b852011-11-24 18:13:56 +020091 [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
92 [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010093
Johannes Bergb9454e82009-07-08 13:29:08 +020094 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010095 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
96 .len = WLAN_MAX_KEY_LEN },
97 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
98 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
99 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen81962262011-11-02 23:36:31 +0200100 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200101 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100102
103 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
104 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
105 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
106 .len = IEEE80211_MAX_DATA_LEN },
107 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
108 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100109 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
110 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
111 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
112 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
113 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100114 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100115 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200116 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100117 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
118 .len = IEEE80211_MAX_MESH_ID_LEN },
119 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300120
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700121 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
122 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
123
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300124 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
125 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
126 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200127 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
128 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100129 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300130
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800131 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700132 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700133
Johannes Berg6c739412011-11-03 09:27:01 +0100134 [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200135
136 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
137 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
138 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100139 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
140 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200141
142 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
143 .len = IEEE80211_MAX_SSID_LEN },
144 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
145 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200146 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300147 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300148 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300149 [NL80211_ATTR_STA_FLAGS2] = {
150 .len = sizeof(struct nl80211_sta_flag_update),
151 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300152 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300153 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
154 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200155 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
156 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
157 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200158 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100159 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100160 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
161 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100162 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
163 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200164 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200165 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
166 .len = IEEE80211_MAX_DATA_LEN },
167 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200168 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200169 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300170 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200171 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300172 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
173 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200174 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900175 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
176 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100177 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100178 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100179 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200180 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700181 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300182 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200183 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200184 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300185 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Jouni Malinen9946ecf2011-08-10 23:55:56 +0300186 [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
187 .len = IEEE80211_MAX_DATA_LEN },
188 [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
189 .len = IEEE80211_MAX_DATA_LEN },
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530190 [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300191 [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530192 [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
Arik Nemtsov109086c2011-09-28 14:12:50 +0300193 [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
194 [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
195 [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
196 [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
197 [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
Johannes Berge247bd902011-11-04 11:18:21 +0100198 [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
Arik Nemtsov00f740e2011-11-10 11:28:56 +0200199 [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
200 .len = IEEE80211_MAX_DATA_LEN },
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -0700201 [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
Ben Greear7e7c8922011-11-18 11:31:59 -0800202 [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
203 [NL80211_ATTR_HT_CAPABILITY_MASK] = {
204 .len = NL80211_HT_CAPABILITY_LEN
205 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100206 [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +0530207 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
Bala Shanmugam4486ea92012-03-07 17:27:12 +0530208 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
Mahesh Palivela5fed1a72013-02-21 08:36:42 +0530209 [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
210 [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
211 [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
212 [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
Johannes Berg55682962007-09-20 13:09:35 -0400213};
214
Johannes Berge31b8212010-10-05 19:39:30 +0200215/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000216static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200217 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200218 [NL80211_KEY_IDX] = { .type = NLA_U8 },
219 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200220 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200221 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
222 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200223 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100224 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
225};
226
227/* policy for the key default flags */
228static const struct nla_policy
229nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
230 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
231 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200232};
233
Johannes Bergff1b6e62011-05-04 15:37:28 +0200234/* policy for WoWLAN attributes */
235static const struct nla_policy
236nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
237 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
238 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
239 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
240 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200241 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
242 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
243 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
244 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200245};
246
Johannes Berge5497d72011-07-05 16:35:40 +0200247/* policy for GTK rekey offload attributes */
248static const struct nla_policy
249nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
250 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
251 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
252 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
253};
254
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300255static const struct nla_policy
256nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
257 [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY,
258 .len = IEEE80211_MAX_SSID_LEN },
259};
260
Holger Schuriga0438972009-11-11 11:30:02 +0100261/* ifidx get helper */
262static int nl80211_get_ifidx(struct netlink_callback *cb)
263{
264 int res;
265
266 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
267 nl80211_fam.attrbuf, nl80211_fam.maxattr,
268 nl80211_policy);
269 if (res)
270 return res;
271
272 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
273 return -EINVAL;
274
275 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
276 if (!res)
277 return -EINVAL;
278 return res;
279}
280
Johannes Berg67748892010-10-04 21:14:06 +0200281static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
282 struct netlink_callback *cb,
283 struct cfg80211_registered_device **rdev,
284 struct net_device **dev)
285{
286 int ifidx = cb->args[0];
287 int err;
288
289 if (!ifidx)
290 ifidx = nl80211_get_ifidx(cb);
291 if (ifidx < 0)
292 return ifidx;
293
294 cb->args[0] = ifidx;
295
296 rtnl_lock();
297
298 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
299 if (!*dev) {
300 err = -ENODEV;
301 goto out_rtnl;
302 }
303
304 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100305 if (IS_ERR(*rdev)) {
306 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200307 goto out_rtnl;
308 }
309
310 return 0;
311 out_rtnl:
312 rtnl_unlock();
313 return err;
314}
315
316static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
317{
318 cfg80211_unlock_rdev(rdev);
319 rtnl_unlock();
320}
321
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100322/* IE validation */
323static bool is_valid_ie_attr(const struct nlattr *attr)
324{
325 const u8 *pos;
326 int len;
327
328 if (!attr)
329 return true;
330
331 pos = nla_data(attr);
332 len = nla_len(attr);
333
334 while (len) {
335 u8 elemlen;
336
337 if (len < 2)
338 return false;
339 len -= 2;
340
341 elemlen = pos[1];
342 if (elemlen > len)
343 return false;
344
345 len -= elemlen;
346 pos += 2 + elemlen;
347 }
348
349 return true;
350}
351
Johannes Berg55682962007-09-20 13:09:35 -0400352/* message building helper */
353static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
354 int flags, u8 cmd)
355{
356 /* since there is no private header just add the generic one */
357 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
358}
359
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400360static int nl80211_msg_put_channel(struct sk_buff *msg,
361 struct ieee80211_channel *chan)
362{
363 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
364 chan->center_freq);
365
366 if (chan->flags & IEEE80211_CHAN_DISABLED)
367 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
368 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
369 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
370 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
371 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
372 if (chan->flags & IEEE80211_CHAN_RADAR)
373 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
374
375 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
376 DBM_TO_MBM(chan->max_power));
377
378 return 0;
379
380 nla_put_failure:
381 return -ENOBUFS;
382}
383
Johannes Berg55682962007-09-20 13:09:35 -0400384/* netlink command implementations */
385
Johannes Bergb9454e82009-07-08 13:29:08 +0200386struct key_parse {
387 struct key_params p;
388 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200389 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200390 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100391 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200392};
393
394static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
395{
396 struct nlattr *tb[NL80211_KEY_MAX + 1];
397 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
398 nl80211_key_policy);
399 if (err)
400 return err;
401
402 k->def = !!tb[NL80211_KEY_DEFAULT];
403 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
404
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100405 if (k->def) {
406 k->def_uni = true;
407 k->def_multi = true;
408 }
409 if (k->defmgmt)
410 k->def_multi = true;
411
Johannes Bergb9454e82009-07-08 13:29:08 +0200412 if (tb[NL80211_KEY_IDX])
413 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
414
415 if (tb[NL80211_KEY_DATA]) {
416 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
417 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
418 }
419
420 if (tb[NL80211_KEY_SEQ]) {
421 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
422 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
423 }
424
425 if (tb[NL80211_KEY_CIPHER])
426 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
427
Johannes Berge31b8212010-10-05 19:39:30 +0200428 if (tb[NL80211_KEY_TYPE]) {
429 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
430 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
431 return -EINVAL;
432 }
433
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100434 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
435 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
Johannes Berg2da8f412012-01-20 13:52:37 +0100436 err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
437 tb[NL80211_KEY_DEFAULT_TYPES],
438 nl80211_key_default_policy);
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100439 if (err)
440 return err;
441
442 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
443 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
444 }
445
Johannes Bergb9454e82009-07-08 13:29:08 +0200446 return 0;
447}
448
449static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
450{
451 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
452 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
453 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
454 }
455
456 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
457 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
458 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
459 }
460
461 if (info->attrs[NL80211_ATTR_KEY_IDX])
462 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
463
464 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
465 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
466
467 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
468 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
469
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100470 if (k->def) {
471 k->def_uni = true;
472 k->def_multi = true;
473 }
474 if (k->defmgmt)
475 k->def_multi = true;
476
Johannes Berge31b8212010-10-05 19:39:30 +0200477 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
478 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
479 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
480 return -EINVAL;
481 }
482
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100483 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
484 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
485 int err = nla_parse_nested(
486 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
487 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
488 nl80211_key_default_policy);
489 if (err)
490 return err;
491
492 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
493 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
494 }
495
Johannes Bergb9454e82009-07-08 13:29:08 +0200496 return 0;
497}
498
499static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
500{
501 int err;
502
503 memset(k, 0, sizeof(*k));
504 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200505 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200506
507 if (info->attrs[NL80211_ATTR_KEY])
508 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
509 else
510 err = nl80211_parse_key_old(info, k);
511
512 if (err)
513 return err;
514
515 if (k->def && k->defmgmt)
516 return -EINVAL;
517
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100518 if (k->defmgmt) {
519 if (k->def_uni || !k->def_multi)
520 return -EINVAL;
521 }
522
Johannes Bergb9454e82009-07-08 13:29:08 +0200523 if (k->idx != -1) {
524 if (k->defmgmt) {
525 if (k->idx < 4 || k->idx > 5)
526 return -EINVAL;
527 } else if (k->def) {
528 if (k->idx < 0 || k->idx > 3)
529 return -EINVAL;
530 } else {
531 if (k->idx < 0 || k->idx > 5)
532 return -EINVAL;
533 }
534 }
535
536 return 0;
537}
538
Johannes Bergfffd0932009-07-08 14:22:54 +0200539static struct cfg80211_cached_keys *
540nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
541 struct nlattr *keys)
542{
543 struct key_parse parse;
544 struct nlattr *key;
545 struct cfg80211_cached_keys *result;
546 int rem, err, def = 0;
547
548 result = kzalloc(sizeof(*result), GFP_KERNEL);
549 if (!result)
550 return ERR_PTR(-ENOMEM);
551
552 result->def = -1;
553 result->defmgmt = -1;
554
555 nla_for_each_nested(key, keys, rem) {
556 memset(&parse, 0, sizeof(parse));
557 parse.idx = -1;
558
559 err = nl80211_parse_key_new(key, &parse);
560 if (err)
561 goto error;
562 err = -EINVAL;
563 if (!parse.p.key)
564 goto error;
565 if (parse.idx < 0 || parse.idx > 4)
566 goto error;
567 if (parse.def) {
568 if (def)
569 goto error;
570 def = 1;
571 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100572 if (!parse.def_uni || !parse.def_multi)
573 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200574 } else if (parse.defmgmt)
575 goto error;
576 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200577 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200578 if (err)
579 goto error;
580 result->params[parse.idx].cipher = parse.p.cipher;
581 result->params[parse.idx].key_len = parse.p.key_len;
582 result->params[parse.idx].key = result->data[parse.idx];
583 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
584 }
585
586 return result;
587 error:
588 kfree(result);
589 return ERR_PTR(err);
590}
591
592static int nl80211_key_allowed(struct wireless_dev *wdev)
593{
594 ASSERT_WDEV_LOCK(wdev);
595
Johannes Bergfffd0932009-07-08 14:22:54 +0200596 switch (wdev->iftype) {
597 case NL80211_IFTYPE_AP:
598 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200599 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700600 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200601 break;
602 case NL80211_IFTYPE_ADHOC:
603 if (!wdev->current_bss)
604 return -ENOLINK;
605 break;
606 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200607 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200608 if (wdev->sme_state != CFG80211_SME_CONNECTED)
609 return -ENOLINK;
610 break;
611 default:
612 return -EINVAL;
613 }
614
615 return 0;
616}
617
Johannes Berg7527a782011-05-13 10:58:57 +0200618static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
619{
620 struct nlattr *nl_modes = nla_nest_start(msg, attr);
621 int i;
622
623 if (!nl_modes)
624 goto nla_put_failure;
625
626 i = 0;
627 while (ifmodes) {
628 if (ifmodes & 1)
629 NLA_PUT_FLAG(msg, i);
630 ifmodes >>= 1;
631 i++;
632 }
633
634 nla_nest_end(msg, nl_modes);
635 return 0;
636
637nla_put_failure:
638 return -ENOBUFS;
639}
640
641static int nl80211_put_iface_combinations(struct wiphy *wiphy,
642 struct sk_buff *msg)
643{
644 struct nlattr *nl_combis;
645 int i, j;
646
647 nl_combis = nla_nest_start(msg,
648 NL80211_ATTR_INTERFACE_COMBINATIONS);
649 if (!nl_combis)
650 goto nla_put_failure;
651
652 for (i = 0; i < wiphy->n_iface_combinations; i++) {
653 const struct ieee80211_iface_combination *c;
654 struct nlattr *nl_combi, *nl_limits;
655
656 c = &wiphy->iface_combinations[i];
657
658 nl_combi = nla_nest_start(msg, i + 1);
659 if (!nl_combi)
660 goto nla_put_failure;
661
662 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
663 if (!nl_limits)
664 goto nla_put_failure;
665
666 for (j = 0; j < c->n_limits; j++) {
667 struct nlattr *nl_limit;
668
669 nl_limit = nla_nest_start(msg, j + 1);
670 if (!nl_limit)
671 goto nla_put_failure;
672 NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
673 c->limits[j].max);
674 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
675 c->limits[j].types))
676 goto nla_put_failure;
677 nla_nest_end(msg, nl_limit);
678 }
679
680 nla_nest_end(msg, nl_limits);
681
682 if (c->beacon_int_infra_match)
683 NLA_PUT_FLAG(msg,
684 NL80211_IFACE_COMB_STA_AP_BI_MATCH);
685 NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
686 c->num_different_channels);
687 NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
688 c->max_interfaces);
689
690 nla_nest_end(msg, nl_combi);
691 }
692
693 nla_nest_end(msg, nl_combis);
694
695 return 0;
696nla_put_failure:
697 return -ENOBUFS;
698}
699
Johannes Berg55682962007-09-20 13:09:35 -0400700static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
701 struct cfg80211_registered_device *dev)
702{
703 void *hdr;
Johannes Bergee688b02008-01-24 19:38:39 +0100704 struct nlattr *nl_bands, *nl_band;
705 struct nlattr *nl_freqs, *nl_freq;
706 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100707 struct nlattr *nl_cmds;
Johannes Bergee688b02008-01-24 19:38:39 +0100708 enum ieee80211_band band;
709 struct ieee80211_channel *chan;
710 struct ieee80211_rate *rate;
711 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200712 const struct ieee80211_txrx_stypes *mgmt_stypes =
713 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400714
715 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
716 if (!hdr)
717 return -1;
718
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500719 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400720 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200721
Johannes Bergf5ea9122009-08-07 16:17:38 +0200722 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
723 cfg80211_rdev_list_generation);
724
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200725 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
726 dev->wiphy.retry_short);
727 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
728 dev->wiphy.retry_long);
729 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
730 dev->wiphy.frag_threshold);
731 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
732 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100733 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
734 dev->wiphy.coverage_class);
Johannes Berg2a519312009-02-10 21:25:55 +0100735 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
736 dev->wiphy.max_scan_ssids);
Luciano Coelho93b6aa62011-07-13 14:57:28 +0300737 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
738 dev->wiphy.max_sched_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200739 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
740 dev->wiphy.max_scan_ie_len);
Luciano Coelho5a865ba2011-07-13 14:57:29 +0300741 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
742 dev->wiphy.max_sched_scan_ie_len);
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300743 NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS,
744 dev->wiphy.max_match_sets);
Johannes Bergee688b02008-01-24 19:38:39 +0100745
Johannes Berge31b8212010-10-05 19:39:30 +0200746 if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
747 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
Javier Cardona15d5dda2011-04-07 15:08:28 -0700748 if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
749 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
Eliad Pellercedb5412011-08-31 11:29:43 +0300750 if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD)
751 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530752 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)
753 NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300754 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)
755 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT);
756 if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)
757 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530758
Johannes Berg25e47c12009-04-02 20:14:06 +0200759 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
760 sizeof(u32) * dev->wiphy.n_cipher_suites,
761 dev->wiphy.cipher_suites);
762
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100763 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
764 dev->wiphy.max_num_pmkids);
765
Johannes Bergc0692b82010-08-27 14:26:53 +0300766 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
767 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
768
Bruno Randolf39fd5de2010-12-16 11:30:28 +0900769 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
770 dev->wiphy.available_antennas_tx);
771 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
772 dev->wiphy.available_antennas_rx);
773
Arik Nemtsov87bbbe22011-11-10 11:28:55 +0200774 if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD)
775 NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
776 dev->wiphy.probe_resp_offload);
777
Bruno Randolf7f531e02010-12-16 11:30:22 +0900778 if ((dev->wiphy.available_antennas_tx ||
779 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900780 u32 tx_ant = 0, rx_ant = 0;
781 int res;
782 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
783 if (!res) {
784 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
785 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
786 }
787 }
788
Johannes Berg7527a782011-05-13 10:58:57 +0200789 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
790 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700791 goto nla_put_failure;
792
Johannes Bergee688b02008-01-24 19:38:39 +0100793 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
794 if (!nl_bands)
795 goto nla_put_failure;
796
797 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
798 if (!dev->wiphy.bands[band])
799 continue;
800
801 nl_band = nla_nest_start(msg, band);
802 if (!nl_band)
803 goto nla_put_failure;
804
Johannes Bergd51626d2008-10-09 12:20:13 +0200805 /* add HT info */
806 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
807 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
808 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
809 &dev->wiphy.bands[band]->ht_cap.mcs);
810 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
811 dev->wiphy.bands[band]->ht_cap.cap);
812 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
813 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
814 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
815 dev->wiphy.bands[band]->ht_cap.ampdu_density);
816 }
817
Mahesh Palivela3254bad2013-02-21 05:28:31 +0530818 /* add VHT info */
819 if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
820 (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
821 sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
822 &dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
823 nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
824 dev->wiphy.bands[band]->vht_cap.cap)))
825 goto nla_put_failure;
826
Johannes Bergee688b02008-01-24 19:38:39 +0100827 /* add frequencies */
828 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
829 if (!nl_freqs)
830 goto nla_put_failure;
831
832 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
833 nl_freq = nla_nest_start(msg, i);
834 if (!nl_freq)
835 goto nla_put_failure;
836
837 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b02008-01-24 19:38:39 +0100838
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400839 if (nl80211_msg_put_channel(msg, chan))
840 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200841
Johannes Bergee688b02008-01-24 19:38:39 +0100842 nla_nest_end(msg, nl_freq);
843 }
844
845 nla_nest_end(msg, nl_freqs);
846
847 /* add bitrates */
848 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
849 if (!nl_rates)
850 goto nla_put_failure;
851
852 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
853 nl_rate = nla_nest_start(msg, i);
854 if (!nl_rate)
855 goto nla_put_failure;
856
857 rate = &dev->wiphy.bands[band]->bitrates[i];
858 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
859 rate->bitrate);
860 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
861 NLA_PUT_FLAG(msg,
862 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
863
864 nla_nest_end(msg, nl_rate);
865 }
866
867 nla_nest_end(msg, nl_rates);
868
869 nla_nest_end(msg, nl_band);
870 }
871 nla_nest_end(msg, nl_bands);
872
Johannes Berg8fdc6212009-03-14 09:34:01 +0100873 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
874 if (!nl_cmds)
875 goto nla_put_failure;
876
877 i = 0;
878#define CMD(op, n) \
879 do { \
880 if (dev->ops->op) { \
881 i++; \
882 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
883 } \
884 } while (0)
885
886 CMD(add_virtual_intf, NEW_INTERFACE);
887 CMD(change_virtual_intf, SET_INTERFACE);
888 CMD(add_key, NEW_KEY);
Johannes Berg88600202012-02-13 15:17:18 +0100889 CMD(start_ap, START_AP);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100890 CMD(add_station, NEW_STATION);
891 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800892 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100893 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200894 CMD(auth, AUTHENTICATE);
895 CMD(assoc, ASSOCIATE);
896 CMD(deauth, DEAUTHENTICATE);
897 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200898 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100899 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100900 CMD(set_pmksa, SET_PMKSA);
901 CMD(del_pmksa, DEL_PMKSA);
902 CMD(flush_pmksa, FLUSH_PMKSA);
Johannes Berg7c4ef712011-11-18 15:33:48 +0100903 if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
904 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200905 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200906 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100907 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100908 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200909 i++;
910 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
911 }
Johannes Bergf444de02010-05-05 15:25:02 +0200912 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400913 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300914 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
915 CMD(tdls_mgmt, TDLS_MGMT);
916 CMD(tdls_oper, TDLS_OPER);
917 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300918 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
919 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +0100920 CMD(probe_client, PROBE_CLIENT);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100921 CMD(set_noack_map, SET_NOACK_MAP);
Johannes Berg5e760232011-11-04 11:18:17 +0100922 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
923 i++;
924 NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
925 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100926
Kalle Valo4745fc02011-11-17 19:06:10 +0200927#ifdef CONFIG_NL80211_TESTMODE
928 CMD(testmode_cmd, TESTMODE);
929#endif
930
Johannes Berg8fdc6212009-03-14 09:34:01 +0100931#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200932
Johannes Berg6829c872009-07-02 09:13:27 +0200933 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200934 i++;
935 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
936 }
937
Johannes Berg6829c872009-07-02 09:13:27 +0200938 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200939 i++;
940 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
941 }
942
Johannes Berg8fdc6212009-03-14 09:34:01 +0100943 nla_nest_end(msg, nl_cmds);
944
Johannes Berg7c4ef712011-11-18 15:33:48 +0100945 if (dev->ops->remain_on_channel &&
946 dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
Johannes Berga2939112010-12-14 17:54:28 +0100947 NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
948 dev->wiphy.max_remain_on_channel_duration);
949
Johannes Berg7c4ef712011-11-18 15:33:48 +0100950 if (dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100951 NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
952
Johannes Berg2e161f72010-08-12 15:38:38 +0200953 if (mgmt_stypes) {
954 u16 stypes;
955 struct nlattr *nl_ftypes, *nl_ifs;
956 enum nl80211_iftype ift;
957
958 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
959 if (!nl_ifs)
960 goto nla_put_failure;
961
962 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
963 nl_ftypes = nla_nest_start(msg, ift);
964 if (!nl_ftypes)
965 goto nla_put_failure;
966 i = 0;
967 stypes = mgmt_stypes[ift].tx;
968 while (stypes) {
969 if (stypes & 1)
970 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
971 (i << 4) | IEEE80211_FTYPE_MGMT);
972 stypes >>= 1;
973 i++;
974 }
975 nla_nest_end(msg, nl_ftypes);
976 }
977
Johannes Berg74b70a42010-08-24 12:15:53 +0200978 nla_nest_end(msg, nl_ifs);
979
Johannes Berg2e161f72010-08-12 15:38:38 +0200980 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
981 if (!nl_ifs)
982 goto nla_put_failure;
983
984 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
985 nl_ftypes = nla_nest_start(msg, ift);
986 if (!nl_ftypes)
987 goto nla_put_failure;
988 i = 0;
989 stypes = mgmt_stypes[ift].rx;
990 while (stypes) {
991 if (stypes & 1)
992 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
993 (i << 4) | IEEE80211_FTYPE_MGMT);
994 stypes >>= 1;
995 i++;
996 }
997 nla_nest_end(msg, nl_ftypes);
998 }
999 nla_nest_end(msg, nl_ifs);
1000 }
1001
Johannes Bergff1b6e62011-05-04 15:37:28 +02001002 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
1003 struct nlattr *nl_wowlan;
1004
1005 nl_wowlan = nla_nest_start(msg,
1006 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
1007 if (!nl_wowlan)
1008 goto nla_put_failure;
1009
1010 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
1011 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
1012 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
1013 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
1014 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
1015 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02001016 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
1017 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
1018 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
1019 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
1020 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
1021 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
1022 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
1023 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
1024 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
1025 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02001026 if (dev->wiphy.wowlan.n_patterns) {
1027 struct nl80211_wowlan_pattern_support pat = {
1028 .max_patterns = dev->wiphy.wowlan.n_patterns,
1029 .min_pattern_len =
1030 dev->wiphy.wowlan.pattern_min_len,
1031 .max_pattern_len =
1032 dev->wiphy.wowlan.pattern_max_len,
1033 };
1034 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1035 sizeof(pat), &pat);
1036 }
1037
1038 nla_nest_end(msg, nl_wowlan);
1039 }
1040
Johannes Berg7527a782011-05-13 10:58:57 +02001041 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1042 dev->wiphy.software_iftypes))
1043 goto nla_put_failure;
1044
1045 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1046 goto nla_put_failure;
1047
Johannes Berg562a7482011-11-07 12:39:33 +01001048 if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME)
1049 NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME,
1050 dev->wiphy.ap_sme_capa);
1051
Johannes Berg1f074bd2011-11-06 14:13:33 +01001052 NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features);
1053
Ben Greear7e7c8922011-11-18 11:31:59 -08001054 if (dev->wiphy.ht_capa_mod_mask)
1055 NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
1056 sizeof(*dev->wiphy.ht_capa_mod_mask),
1057 dev->wiphy.ht_capa_mod_mask);
1058
Johannes Berg55682962007-09-20 13:09:35 -04001059 return genlmsg_end(msg, hdr);
1060
1061 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001062 genlmsg_cancel(msg, hdr);
1063 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001064}
1065
1066static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1067{
1068 int idx = 0;
1069 int start = cb->args[0];
1070 struct cfg80211_registered_device *dev;
1071
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001072 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001073 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001074 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1075 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001076 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001077 continue;
1078 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
1079 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001080 dev) < 0) {
1081 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001082 break;
Julius Volzb4637272008-07-08 14:02:19 +02001083 }
Johannes Berg55682962007-09-20 13:09:35 -04001084 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001085 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001086
1087 cb->args[0] = idx;
1088
1089 return skb->len;
1090}
1091
1092static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1093{
1094 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001095 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001096
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001097 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001098 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001099 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001100
Johannes Berg4c476992010-10-04 21:36:35 +02001101 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1102 nlmsg_free(msg);
1103 return -ENOBUFS;
1104 }
Johannes Berg55682962007-09-20 13:09:35 -04001105
Johannes Berg134e6372009-07-10 09:51:34 +00001106 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001107}
1108
Jouni Malinen31888482008-10-30 16:59:24 +02001109static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1110 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1111 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1112 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1113 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1114 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1115};
1116
1117static int parse_txq_params(struct nlattr *tb[],
1118 struct ieee80211_txq_params *txq_params)
1119{
1120 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
1121 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1122 !tb[NL80211_TXQ_ATTR_AIFS])
1123 return -EINVAL;
1124
1125 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
1126 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1127 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1128 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1129 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1130
1131 return 0;
1132}
1133
Johannes Bergf444de02010-05-05 15:25:02 +02001134static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1135{
1136 /*
1137 * You can only set the channel explicitly for AP, mesh
1138 * and WDS type interfaces; all others have their channel
1139 * managed via their respective "establish a connection"
1140 * command (connect, join, ...)
1141 *
1142 * Monitors are special as they are normally slaved to
1143 * whatever else is going on, so they behave as though
1144 * you tried setting the wiphy channel itself.
1145 */
1146 return !wdev ||
1147 wdev->iftype == NL80211_IFTYPE_AP ||
1148 wdev->iftype == NL80211_IFTYPE_WDS ||
1149 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001150 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1151 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001152}
1153
1154static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1155 struct wireless_dev *wdev,
1156 struct genl_info *info)
1157{
1158 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1159 u32 freq;
1160 int result;
1161
1162 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1163 return -EINVAL;
1164
1165 if (!nl80211_can_set_dev_channel(wdev))
1166 return -EOPNOTSUPP;
1167
1168 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1169 channel_type = nla_get_u32(info->attrs[
1170 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1171 if (channel_type != NL80211_CHAN_NO_HT &&
1172 channel_type != NL80211_CHAN_HT20 &&
1173 channel_type != NL80211_CHAN_HT40PLUS &&
1174 channel_type != NL80211_CHAN_HT40MINUS)
1175 return -EINVAL;
1176 }
1177
1178 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1179
1180 mutex_lock(&rdev->devlist_mtx);
1181 if (wdev) {
1182 wdev_lock(wdev);
1183 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
1184 wdev_unlock(wdev);
1185 } else {
1186 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
1187 }
1188 mutex_unlock(&rdev->devlist_mtx);
1189
1190 return result;
1191}
1192
1193static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1194{
Johannes Berg4c476992010-10-04 21:36:35 +02001195 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1196 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001197
Johannes Berg4c476992010-10-04 21:36:35 +02001198 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001199}
1200
Bill Jordane8347eb2010-10-01 13:54:28 -04001201static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1202{
Johannes Berg43b19952010-10-07 13:10:30 +02001203 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1204 struct net_device *dev = info->user_ptr[1];
1205 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001206 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001207
1208 if (!info->attrs[NL80211_ATTR_MAC])
1209 return -EINVAL;
1210
Johannes Berg43b19952010-10-07 13:10:30 +02001211 if (netif_running(dev))
1212 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001213
Johannes Berg43b19952010-10-07 13:10:30 +02001214 if (!rdev->ops->set_wds_peer)
1215 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001216
Johannes Berg43b19952010-10-07 13:10:30 +02001217 if (wdev->iftype != NL80211_IFTYPE_WDS)
1218 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001219
1220 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001221 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001222}
1223
1224
Johannes Berg55682962007-09-20 13:09:35 -04001225static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1226{
1227 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001228 struct net_device *netdev = NULL;
1229 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001230 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001231 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001232 u32 changed;
1233 u8 retry_short = 0, retry_long = 0;
1234 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001235 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001236
Johannes Bergf444de02010-05-05 15:25:02 +02001237 /*
1238 * Try to find the wiphy and netdev. Normally this
1239 * function shouldn't need the netdev, but this is
1240 * done for backward compatibility -- previously
1241 * setting the channel was done per wiphy, but now
1242 * it is per netdev. Previous userland like hostapd
1243 * also passed a netdev to set_wiphy, so that it is
1244 * possible to let that go to the right netdev!
1245 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001246 mutex_lock(&cfg80211_mutex);
1247
Johannes Bergf444de02010-05-05 15:25:02 +02001248 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1249 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1250
1251 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1252 if (netdev && netdev->ieee80211_ptr) {
1253 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1254 mutex_lock(&rdev->mtx);
1255 } else
1256 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001257 }
1258
Johannes Bergf444de02010-05-05 15:25:02 +02001259 if (!netdev) {
1260 rdev = __cfg80211_rdev_from_info(info);
1261 if (IS_ERR(rdev)) {
1262 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001263 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001264 }
1265 wdev = NULL;
1266 netdev = NULL;
1267 result = 0;
1268
1269 mutex_lock(&rdev->mtx);
1270 } else if (netif_running(netdev) &&
1271 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
1272 wdev = netdev->ieee80211_ptr;
1273 else
1274 wdev = NULL;
1275
1276 /*
1277 * end workaround code, by now the rdev is available
1278 * and locked, and wdev may or may not be NULL.
1279 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001280
1281 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001282 result = cfg80211_dev_rename(
1283 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001284
1285 mutex_unlock(&cfg80211_mutex);
1286
1287 if (result)
1288 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001289
Jouni Malinen31888482008-10-30 16:59:24 +02001290 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1291 struct ieee80211_txq_params txq_params;
1292 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1293
1294 if (!rdev->ops->set_txq_params) {
1295 result = -EOPNOTSUPP;
1296 goto bad_res;
1297 }
1298
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001299 if (!netdev) {
1300 result = -EINVAL;
1301 goto bad_res;
1302 }
1303
Johannes Berg133a3ff2011-11-03 14:50:13 +01001304 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1305 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1306 result = -EINVAL;
1307 goto bad_res;
1308 }
1309
Johannes Berg2b5f8b02012-04-02 10:51:55 +02001310 if (!netif_running(netdev)) {
1311 result = -ENETDOWN;
1312 goto bad_res;
1313 }
1314
Jouni Malinen31888482008-10-30 16:59:24 +02001315 nla_for_each_nested(nl_txq_params,
1316 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1317 rem_txq_params) {
1318 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1319 nla_data(nl_txq_params),
1320 nla_len(nl_txq_params),
1321 txq_params_policy);
1322 result = parse_txq_params(tb, &txq_params);
1323 if (result)
1324 goto bad_res;
1325
1326 result = rdev->ops->set_txq_params(&rdev->wiphy,
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001327 netdev,
Jouni Malinen31888482008-10-30 16:59:24 +02001328 &txq_params);
1329 if (result)
1330 goto bad_res;
1331 }
1332 }
1333
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001334 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001335 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001336 if (result)
1337 goto bad_res;
1338 }
1339
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001340 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1341 enum nl80211_tx_power_setting type;
1342 int idx, mbm = 0;
1343
1344 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001345 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001346 goto bad_res;
1347 }
1348
1349 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1350 type = nla_get_u32(info->attrs[idx]);
1351
1352 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1353 (type != NL80211_TX_POWER_AUTOMATIC)) {
1354 result = -EINVAL;
1355 goto bad_res;
1356 }
1357
1358 if (type != NL80211_TX_POWER_AUTOMATIC) {
1359 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1360 mbm = nla_get_u32(info->attrs[idx]);
1361 }
1362
1363 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1364 if (result)
1365 goto bad_res;
1366 }
1367
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001368 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1369 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1370 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001371 if ((!rdev->wiphy.available_antennas_tx &&
1372 !rdev->wiphy.available_antennas_rx) ||
1373 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001374 result = -EOPNOTSUPP;
1375 goto bad_res;
1376 }
1377
1378 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1379 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1380
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001381 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001382 * available antenna masks, except for the "all" mask */
1383 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1384 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001385 result = -EINVAL;
1386 goto bad_res;
1387 }
1388
Bruno Randolf7f531e02010-12-16 11:30:22 +09001389 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1390 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001391
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001392 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1393 if (result)
1394 goto bad_res;
1395 }
1396
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001397 changed = 0;
1398
1399 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1400 retry_short = nla_get_u8(
1401 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1402 if (retry_short == 0) {
1403 result = -EINVAL;
1404 goto bad_res;
1405 }
1406 changed |= WIPHY_PARAM_RETRY_SHORT;
1407 }
1408
1409 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1410 retry_long = nla_get_u8(
1411 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1412 if (retry_long == 0) {
1413 result = -EINVAL;
1414 goto bad_res;
1415 }
1416 changed |= WIPHY_PARAM_RETRY_LONG;
1417 }
1418
1419 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1420 frag_threshold = nla_get_u32(
1421 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1422 if (frag_threshold < 256) {
1423 result = -EINVAL;
1424 goto bad_res;
1425 }
1426 if (frag_threshold != (u32) -1) {
1427 /*
1428 * Fragments (apart from the last one) are required to
1429 * have even length. Make the fragmentation code
1430 * simpler by stripping LSB should someone try to use
1431 * odd threshold value.
1432 */
1433 frag_threshold &= ~0x1;
1434 }
1435 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1436 }
1437
1438 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1439 rts_threshold = nla_get_u32(
1440 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1441 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1442 }
1443
Lukáš Turek81077e82009-12-21 22:50:47 +01001444 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1445 coverage_class = nla_get_u8(
1446 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1447 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1448 }
1449
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001450 if (changed) {
1451 u8 old_retry_short, old_retry_long;
1452 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001453 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001454
1455 if (!rdev->ops->set_wiphy_params) {
1456 result = -EOPNOTSUPP;
1457 goto bad_res;
1458 }
1459
1460 old_retry_short = rdev->wiphy.retry_short;
1461 old_retry_long = rdev->wiphy.retry_long;
1462 old_frag_threshold = rdev->wiphy.frag_threshold;
1463 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001464 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001465
1466 if (changed & WIPHY_PARAM_RETRY_SHORT)
1467 rdev->wiphy.retry_short = retry_short;
1468 if (changed & WIPHY_PARAM_RETRY_LONG)
1469 rdev->wiphy.retry_long = retry_long;
1470 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1471 rdev->wiphy.frag_threshold = frag_threshold;
1472 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1473 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001474 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1475 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001476
1477 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1478 if (result) {
1479 rdev->wiphy.retry_short = old_retry_short;
1480 rdev->wiphy.retry_long = old_retry_long;
1481 rdev->wiphy.frag_threshold = old_frag_threshold;
1482 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001483 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001484 }
1485 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001486
Johannes Berg306d6112008-12-08 12:39:04 +01001487 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001488 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001489 if (netdev)
1490 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001491 return result;
1492}
1493
1494
1495static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001496 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001497 struct net_device *dev)
1498{
1499 void *hdr;
1500
1501 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1502 if (!hdr)
1503 return -1;
1504
1505 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001506 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001507 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001508 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001509
1510 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1511 rdev->devlist_generation ^
1512 (cfg80211_rdev_list_generation << 2));
1513
Johannes Berg55682962007-09-20 13:09:35 -04001514 return genlmsg_end(msg, hdr);
1515
1516 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001517 genlmsg_cancel(msg, hdr);
1518 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001519}
1520
1521static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1522{
1523 int wp_idx = 0;
1524 int if_idx = 0;
1525 int wp_start = cb->args[0];
1526 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001527 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001528 struct wireless_dev *wdev;
1529
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001530 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001531 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1532 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001533 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001534 if (wp_idx < wp_start) {
1535 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001536 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001537 }
Johannes Berg55682962007-09-20 13:09:35 -04001538 if_idx = 0;
1539
Johannes Bergf5ea9122009-08-07 16:17:38 +02001540 mutex_lock(&rdev->devlist_mtx);
1541 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001542 if (if_idx < if_start) {
1543 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001544 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001545 }
Johannes Berg55682962007-09-20 13:09:35 -04001546 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1547 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001548 rdev, wdev->netdev) < 0) {
1549 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001550 goto out;
1551 }
1552 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001553 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001554 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001555
1556 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001557 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001558 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001559 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001560
1561 cb->args[0] = wp_idx;
1562 cb->args[1] = if_idx;
1563
1564 return skb->len;
1565}
1566
1567static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1568{
1569 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001570 struct cfg80211_registered_device *dev = info->user_ptr[0];
1571 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001572
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001573 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001574 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001575 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001576
Johannes Bergd7264052009-04-19 16:23:20 +02001577 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001578 dev, netdev) < 0) {
1579 nlmsg_free(msg);
1580 return -ENOBUFS;
1581 }
Johannes Berg55682962007-09-20 13:09:35 -04001582
Johannes Berg134e6372009-07-10 09:51:34 +00001583 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001584}
1585
Michael Wu66f7ac52008-01-31 19:48:22 +01001586static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1587 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1588 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1589 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1590 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1591 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1592};
1593
1594static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1595{
1596 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1597 int flag;
1598
1599 *mntrflags = 0;
1600
1601 if (!nla)
1602 return -EINVAL;
1603
1604 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1605 nla, mntr_flags_policy))
1606 return -EINVAL;
1607
1608 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1609 if (flags[flag])
1610 *mntrflags |= (1<<flag);
1611
1612 return 0;
1613}
1614
Johannes Berg9bc383d2009-11-19 11:55:19 +01001615static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001616 struct net_device *netdev, u8 use_4addr,
1617 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001618{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001619 if (!use_4addr) {
Jiri Pirkof350a0a2010-06-15 06:50:45 +00001620 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001621 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001622 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001623 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001624
1625 switch (iftype) {
1626 case NL80211_IFTYPE_AP_VLAN:
1627 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1628 return 0;
1629 break;
1630 case NL80211_IFTYPE_STATION:
1631 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1632 return 0;
1633 break;
1634 default:
1635 break;
1636 }
1637
1638 return -EOPNOTSUPP;
1639}
1640
Johannes Berg55682962007-09-20 13:09:35 -04001641static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1642{
Johannes Berg4c476992010-10-04 21:36:35 +02001643 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001644 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001645 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001646 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001647 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001648 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001649 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001650
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001651 memset(&params, 0, sizeof(params));
1652
Johannes Berg04a773a2009-04-19 21:24:32 +02001653 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001654
Johannes Berg723b0382008-09-16 20:22:09 +02001655 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001656 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001657 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001658 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001659 if (ntype > NL80211_IFTYPE_MAX)
1660 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001661 }
1662
Johannes Berg92ffe052008-09-16 20:39:36 +02001663 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001664 struct wireless_dev *wdev = dev->ieee80211_ptr;
1665
Johannes Berg4c476992010-10-04 21:36:35 +02001666 if (ntype != NL80211_IFTYPE_MESH_POINT)
1667 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001668 if (netif_running(dev))
1669 return -EBUSY;
1670
1671 wdev_lock(wdev);
1672 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1673 IEEE80211_MAX_MESH_ID_LEN);
1674 wdev->mesh_id_up_len =
1675 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1676 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1677 wdev->mesh_id_up_len);
1678 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001679 }
1680
Felix Fietkau8b787642009-11-10 18:53:10 +01001681 if (info->attrs[NL80211_ATTR_4ADDR]) {
1682 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1683 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001684 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001685 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001686 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001687 } else {
1688 params.use_4addr = -1;
1689 }
1690
Johannes Berg92ffe052008-09-16 20:39:36 +02001691 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001692 if (ntype != NL80211_IFTYPE_MONITOR)
1693 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001694 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1695 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001696 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001697 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001698
1699 flags = &_flags;
1700 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001701 }
Johannes Berg3b858752009-03-12 09:55:09 +01001702
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001703 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001704 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001705 else
1706 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001707
Johannes Berg9bc383d2009-11-19 11:55:19 +01001708 if (!err && params.use_4addr != -1)
1709 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1710
Johannes Berg55682962007-09-20 13:09:35 -04001711 return err;
1712}
1713
1714static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1715{
Johannes Berg4c476992010-10-04 21:36:35 +02001716 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001717 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001718 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001719 int err;
1720 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001721 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001722
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001723 memset(&params, 0, sizeof(params));
1724
Johannes Berg55682962007-09-20 13:09:35 -04001725 if (!info->attrs[NL80211_ATTR_IFNAME])
1726 return -EINVAL;
1727
1728 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1729 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1730 if (type > NL80211_IFTYPE_MAX)
1731 return -EINVAL;
1732 }
1733
Johannes Berg79c97e92009-07-07 03:56:12 +02001734 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001735 !(rdev->wiphy.interface_modes & (1 << type)))
1736 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001737
Johannes Berg9bc383d2009-11-19 11:55:19 +01001738 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001739 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001740 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001741 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001742 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001743 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001744
Michael Wu66f7ac52008-01-31 19:48:22 +01001745 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1746 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1747 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001748 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001749 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001750 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001751 if (IS_ERR(dev))
1752 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001753
Johannes Berg29cbe682010-12-03 09:20:44 +01001754 if (type == NL80211_IFTYPE_MESH_POINT &&
1755 info->attrs[NL80211_ATTR_MESH_ID]) {
1756 struct wireless_dev *wdev = dev->ieee80211_ptr;
1757
1758 wdev_lock(wdev);
1759 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1760 IEEE80211_MAX_MESH_ID_LEN);
1761 wdev->mesh_id_up_len =
1762 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1763 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1764 wdev->mesh_id_up_len);
1765 wdev_unlock(wdev);
1766 }
1767
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001768 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001769}
1770
1771static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1772{
Johannes Berg4c476992010-10-04 21:36:35 +02001773 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1774 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001775
Johannes Berg4c476992010-10-04 21:36:35 +02001776 if (!rdev->ops->del_virtual_intf)
1777 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001778
Johannes Berg4c476992010-10-04 21:36:35 +02001779 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001780}
1781
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01001782static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
1783{
1784 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1785 struct net_device *dev = info->user_ptr[1];
1786 u16 noack_map;
1787
1788 if (!info->attrs[NL80211_ATTR_NOACK_MAP])
1789 return -EINVAL;
1790
1791 if (!rdev->ops->set_noack_map)
1792 return -EOPNOTSUPP;
1793
1794 noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
1795
1796 return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
1797}
1798
Johannes Berg41ade002007-12-19 02:03:29 +01001799struct get_key_cookie {
1800 struct sk_buff *msg;
1801 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001802 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001803};
1804
1805static void get_key_callback(void *c, struct key_params *params)
1806{
Johannes Bergb9454e82009-07-08 13:29:08 +02001807 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001808 struct get_key_cookie *cookie = c;
1809
1810 if (params->key)
1811 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1812 params->key_len, params->key);
1813
1814 if (params->seq)
1815 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1816 params->seq_len, params->seq);
1817
1818 if (params->cipher)
1819 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1820 params->cipher);
1821
Johannes Bergb9454e82009-07-08 13:29:08 +02001822 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1823 if (!key)
1824 goto nla_put_failure;
1825
1826 if (params->key)
1827 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1828 params->key_len, params->key);
1829
1830 if (params->seq)
1831 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1832 params->seq_len, params->seq);
1833
1834 if (params->cipher)
1835 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1836 params->cipher);
1837
1838 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1839
1840 nla_nest_end(cookie->msg, key);
1841
Johannes Berg41ade002007-12-19 02:03:29 +01001842 return;
1843 nla_put_failure:
1844 cookie->error = 1;
1845}
1846
1847static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1848{
Johannes Berg4c476992010-10-04 21:36:35 +02001849 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001850 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001851 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001852 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001853 const u8 *mac_addr = NULL;
1854 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001855 struct get_key_cookie cookie = {
1856 .error = 0,
1857 };
1858 void *hdr;
1859 struct sk_buff *msg;
1860
1861 if (info->attrs[NL80211_ATTR_KEY_IDX])
1862 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1863
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001864 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001865 return -EINVAL;
1866
1867 if (info->attrs[NL80211_ATTR_MAC])
1868 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1869
Johannes Berge31b8212010-10-05 19:39:30 +02001870 pairwise = !!mac_addr;
1871 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1872 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1873 if (kt >= NUM_NL80211_KEYTYPES)
1874 return -EINVAL;
1875 if (kt != NL80211_KEYTYPE_GROUP &&
1876 kt != NL80211_KEYTYPE_PAIRWISE)
1877 return -EINVAL;
1878 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1879 }
1880
Johannes Berg4c476992010-10-04 21:36:35 +02001881 if (!rdev->ops->get_key)
1882 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001883
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001884 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001885 if (!msg)
1886 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001887
1888 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1889 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001890 if (IS_ERR(hdr))
1891 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001892
1893 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001894 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001895
1896 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1897 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1898 if (mac_addr)
1899 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1900
Johannes Berge31b8212010-10-05 19:39:30 +02001901 if (pairwise && mac_addr &&
1902 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1903 return -ENOENT;
1904
1905 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1906 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001907
1908 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001909 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001910
1911 if (cookie.error)
1912 goto nla_put_failure;
1913
1914 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001915 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001916
1917 nla_put_failure:
1918 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001919 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001920 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001921 return err;
1922}
1923
1924static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1925{
Johannes Berg4c476992010-10-04 21:36:35 +02001926 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001927 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001928 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001929 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001930
Johannes Bergb9454e82009-07-08 13:29:08 +02001931 err = nl80211_parse_key(info, &key);
1932 if (err)
1933 return err;
1934
1935 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001936 return -EINVAL;
1937
Johannes Bergb9454e82009-07-08 13:29:08 +02001938 /* only support setting default key */
1939 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001940 return -EINVAL;
1941
Johannes Bergfffd0932009-07-08 14:22:54 +02001942 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001943
1944 if (key.def) {
1945 if (!rdev->ops->set_default_key) {
1946 err = -EOPNOTSUPP;
1947 goto out;
1948 }
1949
1950 err = nl80211_key_allowed(dev->ieee80211_ptr);
1951 if (err)
1952 goto out;
1953
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001954 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
1955 key.def_uni, key.def_multi);
1956
1957 if (err)
1958 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02001959
Johannes Berg3d23e342009-09-29 23:27:28 +02001960#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001961 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001962#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001963 } else {
1964 if (key.def_uni || !key.def_multi) {
1965 err = -EINVAL;
1966 goto out;
1967 }
1968
1969 if (!rdev->ops->set_default_mgmt_key) {
1970 err = -EOPNOTSUPP;
1971 goto out;
1972 }
1973
1974 err = nl80211_key_allowed(dev->ieee80211_ptr);
1975 if (err)
1976 goto out;
1977
1978 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
1979 dev, key.idx);
1980 if (err)
1981 goto out;
1982
1983#ifdef CONFIG_CFG80211_WEXT
1984 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
1985#endif
1986 }
1987
1988 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02001989 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001990
Johannes Berg41ade002007-12-19 02:03:29 +01001991 return err;
1992}
1993
1994static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1995{
Johannes Berg4c476992010-10-04 21:36:35 +02001996 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02001997 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001998 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02001999 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02002000 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01002001
Johannes Bergb9454e82009-07-08 13:29:08 +02002002 err = nl80211_parse_key(info, &key);
2003 if (err)
2004 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002005
Johannes Bergb9454e82009-07-08 13:29:08 +02002006 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01002007 return -EINVAL;
2008
Johannes Berg41ade002007-12-19 02:03:29 +01002009 if (info->attrs[NL80211_ATTR_MAC])
2010 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2011
Johannes Berge31b8212010-10-05 19:39:30 +02002012 if (key.type == -1) {
2013 if (mac_addr)
2014 key.type = NL80211_KEYTYPE_PAIRWISE;
2015 else
2016 key.type = NL80211_KEYTYPE_GROUP;
2017 }
2018
2019 /* for now */
2020 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2021 key.type != NL80211_KEYTYPE_GROUP)
2022 return -EINVAL;
2023
Johannes Berg4c476992010-10-04 21:36:35 +02002024 if (!rdev->ops->add_key)
2025 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002026
Johannes Berge31b8212010-10-05 19:39:30 +02002027 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
2028 key.type == NL80211_KEYTYPE_PAIRWISE,
2029 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02002030 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02002031
2032 wdev_lock(dev->ieee80211_ptr);
2033 err = nl80211_key_allowed(dev->ieee80211_ptr);
2034 if (!err)
2035 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02002036 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02002037 mac_addr, &key.p);
2038 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002039
Johannes Berg41ade002007-12-19 02:03:29 +01002040 return err;
2041}
2042
2043static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
2044{
Johannes Berg4c476992010-10-04 21:36:35 +02002045 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002046 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002047 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002048 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002049 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002050
Johannes Bergb9454e82009-07-08 13:29:08 +02002051 err = nl80211_parse_key(info, &key);
2052 if (err)
2053 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002054
2055 if (info->attrs[NL80211_ATTR_MAC])
2056 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2057
Johannes Berge31b8212010-10-05 19:39:30 +02002058 if (key.type == -1) {
2059 if (mac_addr)
2060 key.type = NL80211_KEYTYPE_PAIRWISE;
2061 else
2062 key.type = NL80211_KEYTYPE_GROUP;
2063 }
2064
2065 /* for now */
2066 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2067 key.type != NL80211_KEYTYPE_GROUP)
2068 return -EINVAL;
2069
Johannes Berg4c476992010-10-04 21:36:35 +02002070 if (!rdev->ops->del_key)
2071 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002072
Johannes Bergfffd0932009-07-08 14:22:54 +02002073 wdev_lock(dev->ieee80211_ptr);
2074 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002075
2076 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2077 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2078 err = -ENOENT;
2079
Johannes Bergfffd0932009-07-08 14:22:54 +02002080 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02002081 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
2082 key.type == NL80211_KEYTYPE_PAIRWISE,
2083 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002084
Johannes Berg3d23e342009-09-29 23:27:28 +02002085#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002086 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002087 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002088 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002089 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002090 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2091 }
2092#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002093 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002094
Johannes Berg41ade002007-12-19 02:03:29 +01002095 return err;
2096}
2097
Johannes Berg88600202012-02-13 15:17:18 +01002098static int nl80211_parse_beacon(struct genl_info *info,
2099 struct cfg80211_beacon_data *bcn)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002100{
Johannes Berg88600202012-02-13 15:17:18 +01002101 bool haveinfo = false;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002102
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002103 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2104 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2105 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2106 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002107 return -EINVAL;
2108
Johannes Berg88600202012-02-13 15:17:18 +01002109 memset(bcn, 0, sizeof(*bcn));
Johannes Berged1b6cc2007-12-19 02:03:32 +01002110
Johannes Berged1b6cc2007-12-19 02:03:32 +01002111 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
Johannes Berg88600202012-02-13 15:17:18 +01002112 bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2113 bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2114 if (!bcn->head_len)
2115 return -EINVAL;
2116 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002117 }
2118
2119 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
Johannes Berg88600202012-02-13 15:17:18 +01002120 bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2121 bcn->tail_len =
Johannes Berged1b6cc2007-12-19 02:03:32 +01002122 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
Johannes Berg88600202012-02-13 15:17:18 +01002123 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002124 }
2125
Johannes Berg4c476992010-10-04 21:36:35 +02002126 if (!haveinfo)
2127 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002128
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002129 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg88600202012-02-13 15:17:18 +01002130 bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2131 bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002132 }
2133
2134 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002135 bcn->proberesp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002136 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002137 bcn->proberesp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002138 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2139 }
2140
2141 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002142 bcn->assocresp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002143 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002144 bcn->assocresp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002145 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2146 }
2147
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002148 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002149 bcn->probe_resp =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002150 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002151 bcn->probe_resp_len =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002152 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2153 }
2154
Johannes Berg88600202012-02-13 15:17:18 +01002155 return 0;
2156}
2157
2158static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
2159{
2160 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2161 struct net_device *dev = info->user_ptr[1];
2162 struct wireless_dev *wdev = dev->ieee80211_ptr;
2163 struct cfg80211_ap_settings params;
2164 int err;
2165
2166 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2167 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2168 return -EOPNOTSUPP;
2169
2170 if (!rdev->ops->start_ap)
2171 return -EOPNOTSUPP;
2172
2173 if (wdev->beacon_interval)
2174 return -EALREADY;
2175
2176 memset(&params, 0, sizeof(params));
2177
2178 /* these are required for START_AP */
2179 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2180 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
2181 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2182 return -EINVAL;
2183
2184 err = nl80211_parse_beacon(info, &params.beacon);
2185 if (err)
2186 return err;
2187
2188 params.beacon_interval =
2189 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2190 params.dtim_period =
2191 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2192
2193 err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
2194 if (err)
2195 return err;
2196
2197 /*
2198 * In theory, some of these attributes should be required here
2199 * but since they were not used when the command was originally
2200 * added, keep them optional for old user space programs to let
2201 * them continue to work with drivers that do not need the
2202 * additional information -- drivers must check!
2203 */
2204 if (info->attrs[NL80211_ATTR_SSID]) {
2205 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2206 params.ssid_len =
2207 nla_len(info->attrs[NL80211_ATTR_SSID]);
2208 if (params.ssid_len == 0 ||
2209 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2210 return -EINVAL;
2211 }
2212
2213 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2214 params.hidden_ssid = nla_get_u32(
2215 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2216 if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
2217 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
2218 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2219 return -EINVAL;
2220 }
2221
2222 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2223
2224 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2225 params.auth_type = nla_get_u32(
2226 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2227 if (!nl80211_valid_auth_type(params.auth_type))
2228 return -EINVAL;
2229 } else
2230 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2231
2232 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2233 NL80211_MAX_NR_CIPHER_SUITES);
2234 if (err)
2235 return err;
2236
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +05302237 if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
2238 if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
2239 return -EOPNOTSUPP;
2240 params.inactivity_timeout = nla_get_u16(
2241 info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
2242 }
2243
Johannes Berg88600202012-02-13 15:17:18 +01002244 err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
2245 if (!err)
2246 wdev->beacon_interval = params.beacon_interval;
Johannes Berg56d18932011-05-09 18:41:15 +02002247 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002248}
2249
Johannes Berg88600202012-02-13 15:17:18 +01002250static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
2251{
2252 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2253 struct net_device *dev = info->user_ptr[1];
2254 struct wireless_dev *wdev = dev->ieee80211_ptr;
2255 struct cfg80211_beacon_data params;
2256 int err;
2257
2258 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2259 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2260 return -EOPNOTSUPP;
2261
2262 if (!rdev->ops->change_beacon)
2263 return -EOPNOTSUPP;
2264
2265 if (!wdev->beacon_interval)
2266 return -EINVAL;
2267
2268 err = nl80211_parse_beacon(info, &params);
2269 if (err)
2270 return err;
2271
2272 return rdev->ops->change_beacon(&rdev->wiphy, dev, &params);
2273}
2274
2275static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002276{
Johannes Berg4c476992010-10-04 21:36:35 +02002277 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2278 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002279 struct wireless_dev *wdev = dev->ieee80211_ptr;
2280 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002281
Johannes Berg88600202012-02-13 15:17:18 +01002282 if (!rdev->ops->stop_ap)
Johannes Berg4c476992010-10-04 21:36:35 +02002283 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002284
Johannes Berg074ac8d2010-09-16 14:58:22 +02002285 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002286 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2287 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002288
Johannes Berg88600202012-02-13 15:17:18 +01002289 if (!wdev->beacon_interval)
2290 return -ENOENT;
2291
2292 err = rdev->ops->stop_ap(&rdev->wiphy, dev);
Johannes Berg56d18932011-05-09 18:41:15 +02002293 if (!err)
2294 wdev->beacon_interval = 0;
2295 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002296}
2297
Johannes Berg5727ef12007-12-19 02:03:34 +01002298static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2299 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2300 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2301 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002302 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002303 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Bergd83023d2011-12-14 09:29:15 +01002304 [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002305};
2306
Johannes Bergeccb8e82009-05-11 21:57:56 +03002307static int parse_station_flags(struct genl_info *info,
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002308 enum nl80211_iftype iftype,
Johannes Bergeccb8e82009-05-11 21:57:56 +03002309 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002310{
2311 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002312 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002313 int flag;
2314
Johannes Bergeccb8e82009-05-11 21:57:56 +03002315 /*
2316 * Try parsing the new attribute first so userspace
2317 * can specify both for older kernels.
2318 */
2319 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2320 if (nla) {
2321 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002322
Johannes Bergeccb8e82009-05-11 21:57:56 +03002323 sta_flags = nla_data(nla);
2324 params->sta_flags_mask = sta_flags->mask;
2325 params->sta_flags_set = sta_flags->set;
2326 if ((params->sta_flags_mask |
2327 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2328 return -EINVAL;
2329 return 0;
2330 }
2331
2332 /* if present, parse the old attribute */
2333
2334 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002335 if (!nla)
2336 return 0;
2337
2338 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2339 nla, sta_flags_policy))
2340 return -EINVAL;
2341
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002342 /*
2343 * Only allow certain flags for interface types so that
2344 * other attributes are silently ignored. Remember that
2345 * this is backward compatibility code with old userspace
2346 * and shouldn't be hit in other cases anyway.
2347 */
2348 switch (iftype) {
2349 case NL80211_IFTYPE_AP:
2350 case NL80211_IFTYPE_AP_VLAN:
2351 case NL80211_IFTYPE_P2P_GO:
2352 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2353 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2354 BIT(NL80211_STA_FLAG_WME) |
2355 BIT(NL80211_STA_FLAG_MFP);
2356 break;
2357 case NL80211_IFTYPE_P2P_CLIENT:
2358 case NL80211_IFTYPE_STATION:
2359 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2360 BIT(NL80211_STA_FLAG_TDLS_PEER);
2361 break;
2362 case NL80211_IFTYPE_MESH_POINT:
2363 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2364 BIT(NL80211_STA_FLAG_MFP) |
2365 BIT(NL80211_STA_FLAG_AUTHORIZED);
2366 default:
2367 return -EINVAL;
2368 }
Johannes Berg5727ef12007-12-19 02:03:34 +01002369
2370 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
2371 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03002372 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002373
2374 return 0;
2375}
2376
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002377static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2378 int attr)
2379{
2380 struct nlattr *rate;
2381 u16 bitrate;
2382
2383 rate = nla_nest_start(msg, attr);
2384 if (!rate)
2385 goto nla_put_failure;
2386
2387 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2388 bitrate = cfg80211_calculate_bitrate(info);
2389 if (bitrate > 0)
2390 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
2391
2392 if (info->flags & RATE_INFO_FLAGS_MCS)
2393 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs);
2394 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
2395 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
2396 if (info->flags & RATE_INFO_FLAGS_SHORT_GI)
2397 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
2398
2399 nla_nest_end(msg, rate);
2400 return true;
2401
2402nla_put_failure:
2403 return false;
2404}
2405
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002406static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
John W. Linville66266b32012-03-15 13:25:41 -04002407 int flags,
2408 struct cfg80211_registered_device *rdev,
2409 struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002410 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002411{
2412 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002413 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002414
2415 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2416 if (!hdr)
2417 return -1;
2418
2419 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2420 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
2421
Johannes Bergf5ea9122009-08-07 16:17:38 +02002422 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
2423
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002424 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2425 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002426 goto nla_put_failure;
Mohammed Shafi Shajakhanebe27c92011-04-08 21:24:24 +05302427 if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
2428 NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2429 sinfo->connected_time);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002430 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
2431 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2432 sinfo->inactive_time);
2433 if (sinfo->filled & STATION_INFO_RX_BYTES)
2434 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
2435 sinfo->rx_bytes);
2436 if (sinfo->filled & STATION_INFO_TX_BYTES)
2437 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
2438 sinfo->tx_bytes);
2439 if (sinfo->filled & STATION_INFO_LLID)
2440 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
2441 sinfo->llid);
2442 if (sinfo->filled & STATION_INFO_PLID)
2443 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
2444 sinfo->plid);
2445 if (sinfo->filled & STATION_INFO_PLINK_STATE)
2446 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
2447 sinfo->plink_state);
John W. Linville66266b32012-03-15 13:25:41 -04002448 switch (rdev->wiphy.signal_type) {
2449 case CFG80211_SIGNAL_TYPE_MBM:
2450 if (sinfo->filled & STATION_INFO_SIGNAL)
2451 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
2452 sinfo->signal);
2453 if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
2454 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2455 sinfo->signal_avg);
2456 break;
2457 default:
2458 break;
2459 }
Henning Rogge420e7fa2008-12-11 22:04:19 +01002460 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002461 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2462 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002463 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002464 }
2465 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2466 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2467 NL80211_STA_INFO_RX_BITRATE))
2468 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002469 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02002470 if (sinfo->filled & STATION_INFO_RX_PACKETS)
2471 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
2472 sinfo->rx_packets);
2473 if (sinfo->filled & STATION_INFO_TX_PACKETS)
2474 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
2475 sinfo->tx_packets);
Bruno Randolfb206b4e2010-10-06 18:34:12 +09002476 if (sinfo->filled & STATION_INFO_TX_RETRIES)
2477 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES,
2478 sinfo->tx_retries);
2479 if (sinfo->filled & STATION_INFO_TX_FAILED)
2480 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
2481 sinfo->tx_failed);
Paul Stewarta85e1d52011-12-09 11:01:49 -08002482 if (sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT)
2483 NLA_PUT_U32(msg, NL80211_STA_INFO_BEACON_LOSS,
2484 sinfo->beacon_loss_count);
Paul Stewartf4263c92011-03-31 09:25:41 -07002485 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2486 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2487 if (!bss_param)
2488 goto nla_put_failure;
2489
2490 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
2491 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
2492 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
2493 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
2494 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
2495 NLA_PUT_FLAG(msg,
2496 NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
2497 NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2498 sinfo->bss_param.dtim_period);
2499 NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2500 sinfo->bss_param.beacon_interval);
2501
2502 nla_nest_end(msg, bss_param);
2503 }
Helmut Schaabb6e7532011-10-13 16:30:39 +02002504 if (sinfo->filled & STATION_INFO_STA_FLAGS)
2505 NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS,
2506 sizeof(struct nl80211_sta_flag_update),
2507 &sinfo->sta_flags);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002508 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002509
Felix Fietkau040bdf72011-08-10 19:00:33 -06002510 if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002511 NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2512 sinfo->assoc_req_ies);
2513
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002514 return genlmsg_end(msg, hdr);
2515
2516 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002517 genlmsg_cancel(msg, hdr);
2518 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002519}
2520
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002521static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002522 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002523{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002524 struct station_info sinfo;
2525 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002526 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002527 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002528 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002529 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002530
Johannes Berg67748892010-10-04 21:14:06 +02002531 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2532 if (err)
2533 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002534
2535 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002536 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002537 goto out_err;
2538 }
2539
Johannes Bergbba95fe2008-07-29 13:22:51 +02002540 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002541 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002542 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2543 mac_addr, &sinfo);
2544 if (err == -ENOENT)
2545 break;
2546 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002547 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002548
2549 if (nl80211_send_station(skb,
2550 NETLINK_CB(cb->skb).pid,
2551 cb->nlh->nlmsg_seq, NLM_F_MULTI,
John W. Linville66266b32012-03-15 13:25:41 -04002552 dev, netdev, mac_addr,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002553 &sinfo) < 0)
2554 goto out;
2555
2556 sta_idx++;
2557 }
2558
2559
2560 out:
2561 cb->args[1] = sta_idx;
2562 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002563 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002564 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002565
2566 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002567}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002568
Johannes Berg5727ef12007-12-19 02:03:34 +01002569static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2570{
Johannes Berg4c476992010-10-04 21:36:35 +02002571 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2572 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002573 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002574 struct sk_buff *msg;
2575 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002576 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002577
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002578 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002579
2580 if (!info->attrs[NL80211_ATTR_MAC])
2581 return -EINVAL;
2582
2583 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2584
Johannes Berg4c476992010-10-04 21:36:35 +02002585 if (!rdev->ops->get_station)
2586 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002587
Johannes Berg79c97e92009-07-07 03:56:12 +02002588 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002589 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002590 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002591
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002592 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002593 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002594 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002595
2596 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
John W. Linville66266b32012-03-15 13:25:41 -04002597 rdev, dev, mac_addr, &sinfo) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02002598 nlmsg_free(msg);
2599 return -ENOBUFS;
2600 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002601
Johannes Berg4c476992010-10-04 21:36:35 +02002602 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002603}
2604
2605/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002606 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002607 */
Johannes Berg80b99892011-11-18 16:23:01 +01002608static struct net_device *get_vlan(struct genl_info *info,
2609 struct cfg80211_registered_device *rdev)
Johannes Berg5727ef12007-12-19 02:03:34 +01002610{
Johannes Berg463d0182009-07-14 00:33:35 +02002611 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg80b99892011-11-18 16:23:01 +01002612 struct net_device *v;
2613 int ret;
Johannes Berg5727ef12007-12-19 02:03:34 +01002614
Johannes Berg80b99892011-11-18 16:23:01 +01002615 if (!vlanattr)
2616 return NULL;
2617
2618 v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
2619 if (!v)
2620 return ERR_PTR(-ENODEV);
2621
2622 if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
2623 ret = -EINVAL;
2624 goto error;
Johannes Berg5727ef12007-12-19 02:03:34 +01002625 }
Johannes Berg80b99892011-11-18 16:23:01 +01002626
2627 if (!netif_running(v)) {
2628 ret = -ENETDOWN;
2629 goto error;
2630 }
2631
2632 return v;
2633 error:
2634 dev_put(v);
2635 return ERR_PTR(ret);
Johannes Berg5727ef12007-12-19 02:03:34 +01002636}
2637
2638static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2639{
Johannes Berg4c476992010-10-04 21:36:35 +02002640 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002641 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002642 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002643 struct station_parameters params;
2644 u8 *mac_addr = NULL;
2645
2646 memset(&params, 0, sizeof(params));
2647
2648 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002649 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002650
2651 if (info->attrs[NL80211_ATTR_STA_AID])
2652 return -EINVAL;
2653
2654 if (!info->attrs[NL80211_ATTR_MAC])
2655 return -EINVAL;
2656
2657 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2658
2659 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2660 params.supported_rates =
2661 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2662 params.supported_rates_len =
2663 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2664 }
2665
2666 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2667 params.listen_interval =
2668 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2669
Jouni Malinen36aedc92008-08-25 11:58:58 +03002670 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2671 params.ht_capa =
2672 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2673
Johannes Bergbdd90d52011-12-14 12:20:27 +01002674 if (!rdev->ops->change_station)
2675 return -EOPNOTSUPP;
2676
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002677 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002678 return -EINVAL;
2679
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002680 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2681 params.plink_action =
2682 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2683
Javier Cardona9c3990a2011-05-03 16:57:11 -07002684 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2685 params.plink_state =
2686 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2687
Johannes Berga97f4422009-06-18 17:23:43 +02002688 switch (dev->ieee80211_ptr->iftype) {
2689 case NL80211_IFTYPE_AP:
2690 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002691 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002692 /* disallow mesh-specific things */
2693 if (params.plink_action)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002694 return -EINVAL;
2695
2696 /* TDLS can't be set, ... */
2697 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
2698 return -EINVAL;
2699 /*
2700 * ... but don't bother the driver with it. This works around
2701 * a hostapd/wpa_supplicant issue -- it always includes the
2702 * TLDS_PEER flag in the mask even for AP mode.
2703 */
2704 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2705
2706 /* accept only the listed bits */
2707 if (params.sta_flags_mask &
2708 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2709 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2710 BIT(NL80211_STA_FLAG_WME) |
2711 BIT(NL80211_STA_FLAG_MFP)))
2712 return -EINVAL;
2713
2714 /* must be last in here for error handling */
2715 params.vlan = get_vlan(info, rdev);
2716 if (IS_ERR(params.vlan))
2717 return PTR_ERR(params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002718 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002719 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002720 case NL80211_IFTYPE_STATION:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002721 /*
2722 * Don't allow userspace to change the TDLS_PEER flag,
2723 * but silently ignore attempts to change it since we
2724 * don't have state here to verify that it doesn't try
2725 * to change the flag.
2726 */
2727 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Antonio Quartulli267335d2012-01-31 20:25:47 +01002728 /* fall through */
2729 case NL80211_IFTYPE_ADHOC:
2730 /* disallow things sta doesn't support */
2731 if (params.plink_action)
2732 return -EINVAL;
2733 if (params.ht_capa)
2734 return -EINVAL;
2735 if (params.listen_interval >= 0)
2736 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002737 /* reject any changes other than AUTHORIZED */
2738 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2739 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002740 break;
2741 case NL80211_IFTYPE_MESH_POINT:
2742 /* disallow things mesh doesn't support */
2743 if (params.vlan)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002744 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002745 if (params.ht_capa)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002746 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002747 if (params.listen_interval >= 0)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002748 return -EINVAL;
2749 /*
2750 * No special handling for TDLS here -- the userspace
2751 * mesh code doesn't have this bug.
2752 */
Javier Cardonab39c48f2011-04-07 15:08:30 -07002753 if (params.sta_flags_mask &
2754 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002755 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002756 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Bergbdd90d52011-12-14 12:20:27 +01002757 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002758 break;
2759 default:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002760 return -EOPNOTSUPP;
Johannes Berg034d6552009-05-27 10:35:29 +02002761 }
2762
Johannes Bergbdd90d52011-12-14 12:20:27 +01002763 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01002764
Johannes Berg79c97e92009-07-07 03:56:12 +02002765 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002766
Johannes Berg5727ef12007-12-19 02:03:34 +01002767 if (params.vlan)
2768 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002769
Johannes Berg5727ef12007-12-19 02:03:34 +01002770 return err;
2771}
2772
Eliad Pellerc75786c2011-08-23 14:37:46 +03002773static struct nla_policy
2774nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
2775 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
2776 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
2777};
2778
Johannes Berg5727ef12007-12-19 02:03:34 +01002779static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2780{
Johannes Berg4c476992010-10-04 21:36:35 +02002781 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002782 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002783 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002784 struct station_parameters params;
2785 u8 *mac_addr = NULL;
2786
2787 memset(&params, 0, sizeof(params));
2788
2789 if (!info->attrs[NL80211_ATTR_MAC])
2790 return -EINVAL;
2791
Johannes Berg5727ef12007-12-19 02:03:34 +01002792 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2793 return -EINVAL;
2794
2795 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2796 return -EINVAL;
2797
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002798 if (!info->attrs[NL80211_ATTR_STA_AID])
2799 return -EINVAL;
2800
Johannes Berg5727ef12007-12-19 02:03:34 +01002801 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2802 params.supported_rates =
2803 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2804 params.supported_rates_len =
2805 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2806 params.listen_interval =
2807 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002808
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002809 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2810 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2811 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002812
Jouni Malinen36aedc92008-08-25 11:58:58 +03002813 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2814 params.ht_capa =
2815 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002816
Mahesh Palivela5fed1a72013-02-21 08:36:42 +05302817 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
2818 params.vht_capa =
2819 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
2820
Javier Cardona96b78df2011-04-07 15:08:33 -07002821 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2822 params.plink_action =
2823 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2824
Johannes Bergbdd90d52011-12-14 12:20:27 +01002825 if (!rdev->ops->add_station)
2826 return -EOPNOTSUPP;
2827
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002828 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002829 return -EINVAL;
2830
Johannes Bergbdd90d52011-12-14 12:20:27 +01002831 switch (dev->ieee80211_ptr->iftype) {
2832 case NL80211_IFTYPE_AP:
2833 case NL80211_IFTYPE_AP_VLAN:
2834 case NL80211_IFTYPE_P2P_GO:
2835 /* parse WME attributes if sta is WME capable */
2836 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
2837 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
2838 info->attrs[NL80211_ATTR_STA_WME]) {
2839 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
2840 struct nlattr *nla;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002841
Johannes Bergbdd90d52011-12-14 12:20:27 +01002842 nla = info->attrs[NL80211_ATTR_STA_WME];
2843 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
2844 nl80211_sta_wme_policy);
2845 if (err)
2846 return err;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002847
Johannes Bergbdd90d52011-12-14 12:20:27 +01002848 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
2849 params.uapsd_queues =
2850 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
2851 if (params.uapsd_queues &
2852 ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
2853 return -EINVAL;
2854
2855 if (tb[NL80211_STA_WME_MAX_SP])
2856 params.max_sp =
2857 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
2858
2859 if (params.max_sp &
2860 ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
2861 return -EINVAL;
2862
2863 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
2864 }
2865 /* TDLS peers cannot be added */
2866 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02002867 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002868 /* but don't bother the driver with it */
2869 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Eliad Pellerc75786c2011-08-23 14:37:46 +03002870
Johannes Bergbdd90d52011-12-14 12:20:27 +01002871 /* must be last in here for error handling */
2872 params.vlan = get_vlan(info, rdev);
2873 if (IS_ERR(params.vlan))
2874 return PTR_ERR(params.vlan);
2875 break;
2876 case NL80211_IFTYPE_MESH_POINT:
2877 /* TDLS peers cannot be added */
2878 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02002879 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002880 break;
2881 case NL80211_IFTYPE_STATION:
2882 /* Only TDLS peers can be added */
2883 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
2884 return -EINVAL;
2885 /* Can only add if TDLS ... */
2886 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
2887 return -EOPNOTSUPP;
2888 /* ... with external setup is supported */
2889 if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
2890 return -EOPNOTSUPP;
2891 break;
2892 default:
2893 return -EOPNOTSUPP;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002894 }
2895
Johannes Bergbdd90d52011-12-14 12:20:27 +01002896 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01002897
Johannes Berg79c97e92009-07-07 03:56:12 +02002898 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002899
Johannes Berg5727ef12007-12-19 02:03:34 +01002900 if (params.vlan)
2901 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01002902 return err;
2903}
2904
2905static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2906{
Johannes Berg4c476992010-10-04 21:36:35 +02002907 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2908 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002909 u8 *mac_addr = NULL;
2910
2911 if (info->attrs[NL80211_ATTR_MAC])
2912 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2913
Johannes Berge80cf852009-05-11 14:43:13 +02002914 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002915 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002916 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002917 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2918 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002919
Johannes Berg4c476992010-10-04 21:36:35 +02002920 if (!rdev->ops->del_station)
2921 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01002922
Johannes Berg4c476992010-10-04 21:36:35 +02002923 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002924}
2925
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002926static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2927 int flags, struct net_device *dev,
2928 u8 *dst, u8 *next_hop,
2929 struct mpath_info *pinfo)
2930{
2931 void *hdr;
2932 struct nlattr *pinfoattr;
2933
2934 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2935 if (!hdr)
2936 return -1;
2937
2938 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2939 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2940 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2941
Johannes Bergf5ea9122009-08-07 16:17:38 +02002942 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2943
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002944 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2945 if (!pinfoattr)
2946 goto nla_put_failure;
2947 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2948 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2949 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002950 if (pinfo->filled & MPATH_INFO_SN)
2951 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2952 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002953 if (pinfo->filled & MPATH_INFO_METRIC)
2954 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2955 pinfo->metric);
2956 if (pinfo->filled & MPATH_INFO_EXPTIME)
2957 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2958 pinfo->exptime);
2959 if (pinfo->filled & MPATH_INFO_FLAGS)
2960 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2961 pinfo->flags);
2962 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2963 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2964 pinfo->discovery_timeout);
2965 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2966 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2967 pinfo->discovery_retries);
2968
2969 nla_nest_end(msg, pinfoattr);
2970
2971 return genlmsg_end(msg, hdr);
2972
2973 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002974 genlmsg_cancel(msg, hdr);
2975 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002976}
2977
2978static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002979 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002980{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002981 struct mpath_info pinfo;
2982 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002983 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002984 u8 dst[ETH_ALEN];
2985 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002986 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002987 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002988
Johannes Berg67748892010-10-04 21:14:06 +02002989 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2990 if (err)
2991 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002992
2993 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002994 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002995 goto out_err;
2996 }
2997
Jouni Malineneec60b02009-03-20 21:21:19 +02002998 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2999 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02003000 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02003001 }
3002
Johannes Bergbba95fe2008-07-29 13:22:51 +02003003 while (1) {
3004 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
3005 dst, next_hop, &pinfo);
3006 if (err == -ENOENT)
3007 break;
3008 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003009 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003010
3011 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
3012 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3013 netdev, dst, next_hop,
3014 &pinfo) < 0)
3015 goto out;
3016
3017 path_idx++;
3018 }
3019
3020
3021 out:
3022 cb->args[1] = path_idx;
3023 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003024 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003025 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003026 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003027}
3028
3029static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
3030{
Johannes Berg4c476992010-10-04 21:36:35 +02003031 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003032 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003033 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003034 struct mpath_info pinfo;
3035 struct sk_buff *msg;
3036 u8 *dst = NULL;
3037 u8 next_hop[ETH_ALEN];
3038
3039 memset(&pinfo, 0, sizeof(pinfo));
3040
3041 if (!info->attrs[NL80211_ATTR_MAC])
3042 return -EINVAL;
3043
3044 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3045
Johannes Berg4c476992010-10-04 21:36:35 +02003046 if (!rdev->ops->get_mpath)
3047 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003048
Johannes Berg4c476992010-10-04 21:36:35 +02003049 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3050 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003051
Johannes Berg79c97e92009-07-07 03:56:12 +02003052 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003053 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003054 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003055
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003056 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003057 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003058 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003059
3060 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02003061 dev, dst, next_hop, &pinfo) < 0) {
3062 nlmsg_free(msg);
3063 return -ENOBUFS;
3064 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003065
Johannes Berg4c476992010-10-04 21:36:35 +02003066 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003067}
3068
3069static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
3070{
Johannes Berg4c476992010-10-04 21:36:35 +02003071 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3072 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003073 u8 *dst = NULL;
3074 u8 *next_hop = NULL;
3075
3076 if (!info->attrs[NL80211_ATTR_MAC])
3077 return -EINVAL;
3078
3079 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3080 return -EINVAL;
3081
3082 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3083 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3084
Johannes Berg4c476992010-10-04 21:36:35 +02003085 if (!rdev->ops->change_mpath)
3086 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003087
Johannes Berg4c476992010-10-04 21:36:35 +02003088 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3089 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003090
Johannes Berg4c476992010-10-04 21:36:35 +02003091 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003092}
Johannes Berg4c476992010-10-04 21:36:35 +02003093
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003094static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
3095{
Johannes Berg4c476992010-10-04 21:36:35 +02003096 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3097 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003098 u8 *dst = NULL;
3099 u8 *next_hop = NULL;
3100
3101 if (!info->attrs[NL80211_ATTR_MAC])
3102 return -EINVAL;
3103
3104 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3105 return -EINVAL;
3106
3107 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3108 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3109
Johannes Berg4c476992010-10-04 21:36:35 +02003110 if (!rdev->ops->add_mpath)
3111 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003112
Johannes Berg4c476992010-10-04 21:36:35 +02003113 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3114 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003115
Johannes Berg4c476992010-10-04 21:36:35 +02003116 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003117}
3118
3119static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
3120{
Johannes Berg4c476992010-10-04 21:36:35 +02003121 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3122 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003123 u8 *dst = NULL;
3124
3125 if (info->attrs[NL80211_ATTR_MAC])
3126 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3127
Johannes Berg4c476992010-10-04 21:36:35 +02003128 if (!rdev->ops->del_mpath)
3129 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003130
Johannes Berg4c476992010-10-04 21:36:35 +02003131 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003132}
3133
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003134static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
3135{
Johannes Berg4c476992010-10-04 21:36:35 +02003136 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3137 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003138 struct bss_parameters params;
3139
3140 memset(&params, 0, sizeof(params));
3141 /* default to not changing parameters */
3142 params.use_cts_prot = -1;
3143 params.use_short_preamble = -1;
3144 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003145 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01003146 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003147
3148 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
3149 params.use_cts_prot =
3150 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
3151 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
3152 params.use_short_preamble =
3153 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
3154 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
3155 params.use_short_slot_time =
3156 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02003157 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
3158 params.basic_rates =
3159 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3160 params.basic_rates_len =
3161 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3162 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003163 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
3164 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01003165 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
3166 params.ht_opmode =
3167 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003168
Johannes Berg4c476992010-10-04 21:36:35 +02003169 if (!rdev->ops->change_bss)
3170 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003171
Johannes Berg074ac8d2010-09-16 14:58:22 +02003172 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02003173 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3174 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003175
Johannes Berg4c476992010-10-04 21:36:35 +02003176 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003177}
3178
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003179static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003180 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
3181 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
3182 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
3183 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
3184 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
3185 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
3186};
3187
3188static int parse_reg_rule(struct nlattr *tb[],
3189 struct ieee80211_reg_rule *reg_rule)
3190{
3191 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
3192 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
3193
3194 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
3195 return -EINVAL;
3196 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
3197 return -EINVAL;
3198 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
3199 return -EINVAL;
3200 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
3201 return -EINVAL;
3202 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
3203 return -EINVAL;
3204
3205 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
3206
3207 freq_range->start_freq_khz =
3208 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
3209 freq_range->end_freq_khz =
3210 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
3211 freq_range->max_bandwidth_khz =
3212 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
3213
3214 power_rule->max_eirp =
3215 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
3216
3217 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
3218 power_rule->max_antenna_gain =
3219 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
3220
3221 return 0;
3222}
3223
3224static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3225{
3226 int r;
3227 char *data = NULL;
3228
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003229 /*
3230 * You should only get this when cfg80211 hasn't yet initialized
3231 * completely when built-in to the kernel right between the time
3232 * window between nl80211_init() and regulatory_init(), if that is
3233 * even possible.
3234 */
3235 mutex_lock(&cfg80211_mutex);
3236 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003237 mutex_unlock(&cfg80211_mutex);
3238 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003239 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003240 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003241
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003242 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3243 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003244
3245 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3246
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003247 r = regulatory_hint_user(data);
3248
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003249 return r;
3250}
3251
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003252static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003253 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003254{
Johannes Berg4c476992010-10-04 21:36:35 +02003255 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003256 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003257 struct wireless_dev *wdev = dev->ieee80211_ptr;
3258 struct mesh_config cur_params;
3259 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003260 void *hdr;
3261 struct nlattr *pinfoattr;
3262 struct sk_buff *msg;
3263
Johannes Berg29cbe682010-12-03 09:20:44 +01003264 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3265 return -EOPNOTSUPP;
3266
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003267 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02003268 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02003269
Johannes Berg29cbe682010-12-03 09:20:44 +01003270 wdev_lock(wdev);
3271 /* If not connected, get default parameters */
3272 if (!wdev->mesh_id_len)
3273 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
3274 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003275 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003276 &cur_params);
3277 wdev_unlock(wdev);
3278
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003279 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003280 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003281
3282 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003283 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003284 if (!msg)
3285 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003286 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003287 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003288 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003289 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003290 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003291 if (!pinfoattr)
3292 goto nla_put_failure;
3293 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3294 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
3295 cur_params.dot11MeshRetryTimeout);
3296 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3297 cur_params.dot11MeshConfirmTimeout);
3298 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
3299 cur_params.dot11MeshHoldingTimeout);
3300 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
3301 cur_params.dot11MeshMaxPeerLinks);
3302 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
3303 cur_params.dot11MeshMaxRetries);
3304 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
3305 cur_params.dot11MeshTTL);
Javier Cardona45904f22010-12-03 09:20:40 +01003306 NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL,
3307 cur_params.element_ttl);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003308 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3309 cur_params.auto_open_plinks);
3310 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3311 cur_params.dot11MeshHWMPmaxPREQretries);
3312 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3313 cur_params.path_refresh_time);
3314 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3315 cur_params.min_discovery_timeout);
3316 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3317 cur_params.dot11MeshHWMPactivePathTimeout);
3318 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3319 cur_params.dot11MeshHWMPpreqMinInterval);
Thomas Pedersendca7e942011-11-24 17:15:24 -08003320 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3321 cur_params.dot11MeshHWMPperrMinInterval);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003322 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3323 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00003324 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3325 cur_params.dot11MeshHWMPRootMode);
Javier Cardona0507e152011-08-09 16:45:10 -07003326 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3327 cur_params.dot11MeshHWMPRannInterval);
Javier Cardona16dd7262011-08-09 16:45:11 -07003328 NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3329 cur_params.dot11MeshGateAnnouncementProtocol);
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003330 NLA_PUT_U8(msg, NL80211_MESHCONF_FORWARDING,
3331 cur_params.dot11MeshForwarding);
Ashok Nagarajan55335132012-02-28 17:04:08 -08003332 NLA_PUT_U32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
3333 cur_params.rssi_threshold);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003334 nla_nest_end(msg, pinfoattr);
3335 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003336 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003337
Johannes Berg3b858752009-03-12 09:55:09 +01003338 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003339 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003340 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003341 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003342 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003343}
3344
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003345static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003346 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3347 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3348 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3349 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3350 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3351 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003352 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003353 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
3354
3355 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3356 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3357 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3358 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3359 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
Thomas Pedersendca7e942011-11-24 17:15:24 -08003360 [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003361 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07003362 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07003363 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07003364 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003365 [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
Ashok Nagarajan55335132012-02-28 17:04:08 -08003366 [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32},
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003367};
3368
Javier Cardonac80d5452010-12-16 17:37:49 -08003369static const struct nla_policy
3370 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
3371 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3372 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003373 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003374 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Javier Cardonac80d5452010-12-16 17:37:49 -08003375 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003376 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003377};
3378
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003379static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003380 struct mesh_config *cfg,
3381 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003382{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003383 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003384 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003385
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003386#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3387do {\
3388 if (table[attr_num]) {\
3389 cfg->param = nla_fn(table[attr_num]); \
3390 mask |= (1 << (attr_num - 1)); \
3391 } \
3392} while (0);\
3393
3394
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003395 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003396 return -EINVAL;
3397 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003398 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003399 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003400 return -EINVAL;
3401
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003402 /* This makes sure that there aren't more than 32 mesh config
3403 * parameters (otherwise our bitfield scheme would not work.) */
3404 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3405
3406 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003407 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
3408 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
3409 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
3410 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
3411 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
3412 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
3413 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
3414 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
3415 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
3416 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
3417 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
3418 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003419 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
3420 mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003421 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
3422 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
3423 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
3424 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3425 nla_get_u8);
3426 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
3427 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
3428 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
3429 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3430 nla_get_u16);
3431 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
3432 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3433 nla_get_u32);
3434 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
3435 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3436 nla_get_u16);
Thomas Pedersendca7e942011-11-24 17:15:24 -08003437 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
3438 mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3439 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003440 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3441 dot11MeshHWMPnetDiameterTraversalTime,
3442 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3443 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003444 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3445 dot11MeshHWMPRootMode, mask,
3446 NL80211_MESHCONF_HWMP_ROOTMODE,
3447 nla_get_u8);
Javier Cardona0507e152011-08-09 16:45:10 -07003448 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3449 dot11MeshHWMPRannInterval, mask,
3450 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3451 nla_get_u16);
Javier Cardona16dd7262011-08-09 16:45:11 -07003452 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3453 dot11MeshGateAnnouncementProtocol, mask,
3454 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3455 nla_get_u8);
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003456 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
3457 mask, NL80211_MESHCONF_FORWARDING, nla_get_u8);
Ashok Nagarajan55335132012-02-28 17:04:08 -08003458 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold,
3459 mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003460 if (mask_out)
3461 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003462
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003463 return 0;
3464
3465#undef FILL_IN_MESH_PARAM_IF_SET
3466}
3467
Javier Cardonac80d5452010-12-16 17:37:49 -08003468static int nl80211_parse_mesh_setup(struct genl_info *info,
3469 struct mesh_setup *setup)
3470{
3471 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3472
3473 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3474 return -EINVAL;
3475 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3476 info->attrs[NL80211_ATTR_MESH_SETUP],
3477 nl80211_mesh_setup_params_policy))
3478 return -EINVAL;
3479
3480 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3481 setup->path_sel_proto =
3482 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3483 IEEE80211_PATH_PROTOCOL_VENDOR :
3484 IEEE80211_PATH_PROTOCOL_HWMP;
3485
3486 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3487 setup->path_metric =
3488 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3489 IEEE80211_PATH_METRIC_VENDOR :
3490 IEEE80211_PATH_METRIC_AIRTIME;
3491
Javier Cardona581a8b02011-04-07 15:08:27 -07003492
3493 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003494 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003495 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003496 if (!is_valid_ie_attr(ieattr))
3497 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003498 setup->ie = nla_data(ieattr);
3499 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003500 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003501 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3502 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003503
3504 return 0;
3505}
3506
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003507static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003508 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003509{
3510 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3511 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003512 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003513 struct mesh_config cfg;
3514 u32 mask;
3515 int err;
3516
Johannes Berg29cbe682010-12-03 09:20:44 +01003517 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3518 return -EOPNOTSUPP;
3519
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003520 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003521 return -EOPNOTSUPP;
3522
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003523 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003524 if (err)
3525 return err;
3526
Johannes Berg29cbe682010-12-03 09:20:44 +01003527 wdev_lock(wdev);
3528 if (!wdev->mesh_id_len)
3529 err = -ENOLINK;
3530
3531 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003532 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003533 mask, &cfg);
3534
3535 wdev_unlock(wdev);
3536
3537 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003538}
3539
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003540static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3541{
3542 struct sk_buff *msg;
3543 void *hdr = NULL;
3544 struct nlattr *nl_reg_rules;
3545 unsigned int i;
3546 int err = -EINVAL;
3547
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003548 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003549
3550 if (!cfg80211_regdomain)
3551 goto out;
3552
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003553 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003554 if (!msg) {
3555 err = -ENOBUFS;
3556 goto out;
3557 }
3558
3559 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3560 NL80211_CMD_GET_REG);
3561 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003562 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003563
3564 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3565 cfg80211_regdomain->alpha2);
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003566 if (cfg80211_regdomain->dfs_region)
3567 NLA_PUT_U8(msg, NL80211_ATTR_DFS_REGION,
3568 cfg80211_regdomain->dfs_region);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003569
3570 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3571 if (!nl_reg_rules)
3572 goto nla_put_failure;
3573
3574 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3575 struct nlattr *nl_reg_rule;
3576 const struct ieee80211_reg_rule *reg_rule;
3577 const struct ieee80211_freq_range *freq_range;
3578 const struct ieee80211_power_rule *power_rule;
3579
3580 reg_rule = &cfg80211_regdomain->reg_rules[i];
3581 freq_range = &reg_rule->freq_range;
3582 power_rule = &reg_rule->power_rule;
3583
3584 nl_reg_rule = nla_nest_start(msg, i);
3585 if (!nl_reg_rule)
3586 goto nla_put_failure;
3587
3588 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3589 reg_rule->flags);
3590 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3591 freq_range->start_freq_khz);
3592 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3593 freq_range->end_freq_khz);
3594 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3595 freq_range->max_bandwidth_khz);
3596 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3597 power_rule->max_antenna_gain);
3598 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3599 power_rule->max_eirp);
3600
3601 nla_nest_end(msg, nl_reg_rule);
3602 }
3603
3604 nla_nest_end(msg, nl_reg_rules);
3605
3606 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003607 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003608 goto out;
3609
3610nla_put_failure:
3611 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003612put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003613 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003614 err = -EMSGSIZE;
3615out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003616 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003617 return err;
3618}
3619
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003620static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3621{
3622 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3623 struct nlattr *nl_reg_rule;
3624 char *alpha2 = NULL;
3625 int rem_reg_rules = 0, r = 0;
3626 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003627 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003628 struct ieee80211_regdomain *rd = NULL;
3629
3630 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3631 return -EINVAL;
3632
3633 if (!info->attrs[NL80211_ATTR_REG_RULES])
3634 return -EINVAL;
3635
3636 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3637
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003638 if (info->attrs[NL80211_ATTR_DFS_REGION])
3639 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
3640
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003641 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3642 rem_reg_rules) {
3643 num_rules++;
3644 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003645 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003646 }
3647
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003648 mutex_lock(&cfg80211_mutex);
3649
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003650 if (!reg_is_valid_request(alpha2)) {
3651 r = -EINVAL;
3652 goto bad_reg;
3653 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003654
3655 size_of_regd = sizeof(struct ieee80211_regdomain) +
3656 (num_rules * sizeof(struct ieee80211_reg_rule));
3657
3658 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003659 if (!rd) {
3660 r = -ENOMEM;
3661 goto bad_reg;
3662 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003663
3664 rd->n_reg_rules = num_rules;
3665 rd->alpha2[0] = alpha2[0];
3666 rd->alpha2[1] = alpha2[1];
3667
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003668 /*
3669 * Disable DFS master mode if the DFS region was
3670 * not supported or known on this kernel.
3671 */
3672 if (reg_supported_dfs_region(dfs_region))
3673 rd->dfs_region = dfs_region;
3674
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003675 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3676 rem_reg_rules) {
3677 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3678 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3679 reg_rule_policy);
3680 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3681 if (r)
3682 goto bad_reg;
3683
3684 rule_idx++;
3685
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003686 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3687 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003688 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003689 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003690 }
3691
3692 BUG_ON(rule_idx != num_rules);
3693
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003694 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003695
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003696 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003697
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003698 return r;
3699
Johannes Bergd2372b32008-10-24 20:32:20 +02003700 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003701 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003702 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003703 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003704}
3705
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003706static int validate_scan_freqs(struct nlattr *freqs)
3707{
3708 struct nlattr *attr1, *attr2;
3709 int n_channels = 0, tmp1, tmp2;
3710
3711 nla_for_each_nested(attr1, freqs, tmp1) {
3712 n_channels++;
3713 /*
3714 * Some hardware has a limited channel list for
3715 * scanning, and it is pretty much nonsensical
3716 * to scan for a channel twice, so disallow that
3717 * and don't require drivers to check that the
3718 * channel list they get isn't longer than what
3719 * they can scan, as long as they can scan all
3720 * the channels they registered at once.
3721 */
3722 nla_for_each_nested(attr2, freqs, tmp2)
3723 if (attr1 != attr2 &&
3724 nla_get_u32(attr1) == nla_get_u32(attr2))
3725 return 0;
3726 }
3727
3728 return n_channels;
3729}
3730
Johannes Berg2a519312009-02-10 21:25:55 +01003731static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3732{
Johannes Berg4c476992010-10-04 21:36:35 +02003733 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3734 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003735 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003736 struct nlattr *attr;
3737 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003738 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003739 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003740
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003741 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3742 return -EINVAL;
3743
Johannes Berg79c97e92009-07-07 03:56:12 +02003744 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003745
Johannes Berg4c476992010-10-04 21:36:35 +02003746 if (!rdev->ops->scan)
3747 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003748
Johannes Berg4c476992010-10-04 21:36:35 +02003749 if (rdev->scan_req)
3750 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003751
3752 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003753 n_channels = validate_scan_freqs(
3754 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003755 if (!n_channels)
3756 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003757 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003758 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003759 n_channels = 0;
3760
Johannes Berg2a519312009-02-10 21:25:55 +01003761 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3762 if (wiphy->bands[band])
3763 n_channels += wiphy->bands[band]->n_channels;
3764 }
3765
3766 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3767 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3768 n_ssids++;
3769
Johannes Berg4c476992010-10-04 21:36:35 +02003770 if (n_ssids > wiphy->max_scan_ssids)
3771 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003772
Jouni Malinen70692ad2009-02-16 19:39:13 +02003773 if (info->attrs[NL80211_ATTR_IE])
3774 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3775 else
3776 ie_len = 0;
3777
Johannes Berg4c476992010-10-04 21:36:35 +02003778 if (ie_len > wiphy->max_scan_ie_len)
3779 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003780
Johannes Berg2a519312009-02-10 21:25:55 +01003781 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003782 + sizeof(*request->ssids) * n_ssids
3783 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003784 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003785 if (!request)
3786 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003787
Johannes Berg2a519312009-02-10 21:25:55 +01003788 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003789 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003790 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003791 if (ie_len) {
3792 if (request->ssids)
3793 request->ie = (void *)(request->ssids + n_ssids);
3794 else
3795 request->ie = (void *)(request->channels + n_channels);
3796 }
Johannes Berg2a519312009-02-10 21:25:55 +01003797
Johannes Berg584991d2009-11-02 13:32:03 +01003798 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003799 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3800 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003801 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003802 struct ieee80211_channel *chan;
3803
3804 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3805
3806 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003807 err = -EINVAL;
3808 goto out_free;
3809 }
Johannes Berg584991d2009-11-02 13:32:03 +01003810
3811 /* ignore disabled channels */
3812 if (chan->flags & IEEE80211_CHAN_DISABLED)
3813 continue;
3814
3815 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003816 i++;
3817 }
3818 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003819 enum ieee80211_band band;
3820
Johannes Berg2a519312009-02-10 21:25:55 +01003821 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003822 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3823 int j;
3824 if (!wiphy->bands[band])
3825 continue;
3826 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003827 struct ieee80211_channel *chan;
3828
3829 chan = &wiphy->bands[band]->channels[j];
3830
3831 if (chan->flags & IEEE80211_CHAN_DISABLED)
3832 continue;
3833
3834 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003835 i++;
3836 }
3837 }
3838 }
3839
Johannes Berg584991d2009-11-02 13:32:03 +01003840 if (!i) {
3841 err = -EINVAL;
3842 goto out_free;
3843 }
3844
3845 request->n_channels = i;
3846
Johannes Berg2a519312009-02-10 21:25:55 +01003847 i = 0;
3848 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3849 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003850 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01003851 err = -EINVAL;
3852 goto out_free;
3853 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003854 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01003855 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01003856 i++;
3857 }
3858 }
3859
Jouni Malinen70692ad2009-02-16 19:39:13 +02003860 if (info->attrs[NL80211_ATTR_IE]) {
3861 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003862 memcpy((void *)request->ie,
3863 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003864 request->ie_len);
3865 }
Sunil Dutt Undekari08166fc2012-03-05 21:01:23 +05303866 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Sunil Dutt Undekariae86f142012-03-16 20:17:03 +05303867 if (wiphy->bands[i])
3868 request->rates[i] =
3869 (1 << wiphy->bands[i]->n_bitrates) - 1;
Sunil Dutt Undekari08166fc2012-03-05 21:01:23 +05303870 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
3871 nla_for_each_nested(attr,
3872 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
3873 tmp) {
3874 enum ieee80211_band band = nla_type(attr);
3875 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
3876 err = -EINVAL;
3877 goto out_free;
3878 }
3879 err = ieee80211_get_ratemask(wiphy->bands[band],
3880 nla_data(attr),
3881 nla_len(attr),
3882 &request->rates[band]);
3883 if (err)
3884 goto out_free;
3885 }
3886 }
Jouni Malinen70692ad2009-02-16 19:39:13 +02003887
Johannes Berg34850ab2011-07-18 18:08:35 +02003888 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02003889 if (wiphy->bands[i])
3890 request->rates[i] =
3891 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02003892
3893 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
3894 nla_for_each_nested(attr,
3895 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
3896 tmp) {
3897 enum ieee80211_band band = nla_type(attr);
3898
Dan Carpenter84404622011-07-29 11:52:18 +03003899 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02003900 err = -EINVAL;
3901 goto out_free;
3902 }
3903 err = ieee80211_get_ratemask(wiphy->bands[band],
3904 nla_data(attr),
3905 nla_len(attr),
3906 &request->rates[band]);
3907 if (err)
3908 goto out_free;
3909 }
3910 }
3911
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05303912 request->no_cck =
3913 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
3914
Johannes Berg463d0182009-07-14 00:33:35 +02003915 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003916 request->wiphy = &rdev->wiphy;
Sunil Dutt Undekari08166fc2012-03-05 21:01:23 +05303917 request->no_cck =
3918 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
Johannes Berg2a519312009-02-10 21:25:55 +01003919
Johannes Berg79c97e92009-07-07 03:56:12 +02003920 rdev->scan_req = request;
3921 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003922
Johannes Berg463d0182009-07-14 00:33:35 +02003923 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003924 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003925 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02003926 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01003927 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02003928 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003929 kfree(request);
3930 }
Johannes Berg3b858752009-03-12 09:55:09 +01003931
Johannes Berg2a519312009-02-10 21:25:55 +01003932 return err;
3933}
3934
Luciano Coelho807f8a82011-05-11 17:09:35 +03003935static int nl80211_start_sched_scan(struct sk_buff *skb,
3936 struct genl_info *info)
3937{
3938 struct cfg80211_sched_scan_request *request;
3939 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3940 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003941 struct nlattr *attr;
3942 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003943 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003944 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003945 enum ieee80211_band band;
3946 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003947 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003948
3949 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3950 !rdev->ops->sched_scan_start)
3951 return -EOPNOTSUPP;
3952
3953 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3954 return -EINVAL;
3955
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003956 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
3957 return -EINVAL;
3958
3959 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
3960 if (interval == 0)
3961 return -EINVAL;
3962
Luciano Coelho807f8a82011-05-11 17:09:35 +03003963 wiphy = &rdev->wiphy;
3964
3965 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3966 n_channels = validate_scan_freqs(
3967 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3968 if (!n_channels)
3969 return -EINVAL;
3970 } else {
3971 n_channels = 0;
3972
3973 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3974 if (wiphy->bands[band])
3975 n_channels += wiphy->bands[band]->n_channels;
3976 }
3977
3978 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3979 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3980 tmp)
3981 n_ssids++;
3982
Luciano Coelho93b6aa62011-07-13 14:57:28 +03003983 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003984 return -EINVAL;
3985
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003986 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
3987 nla_for_each_nested(attr,
3988 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
3989 tmp)
3990 n_match_sets++;
3991
3992 if (n_match_sets > wiphy->max_match_sets)
3993 return -EINVAL;
3994
Luciano Coelho807f8a82011-05-11 17:09:35 +03003995 if (info->attrs[NL80211_ATTR_IE])
3996 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3997 else
3998 ie_len = 0;
3999
Luciano Coelho5a865ba2011-07-13 14:57:29 +03004000 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004001 return -EINVAL;
4002
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004003 mutex_lock(&rdev->sched_scan_mtx);
4004
4005 if (rdev->sched_scan_req) {
4006 err = -EINPROGRESS;
4007 goto out;
4008 }
4009
Luciano Coelho807f8a82011-05-11 17:09:35 +03004010 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004011 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004012 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004013 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03004014 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004015 if (!request) {
4016 err = -ENOMEM;
4017 goto out;
4018 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03004019
4020 if (n_ssids)
4021 request->ssids = (void *)&request->channels[n_channels];
4022 request->n_ssids = n_ssids;
4023 if (ie_len) {
4024 if (request->ssids)
4025 request->ie = (void *)(request->ssids + n_ssids);
4026 else
4027 request->ie = (void *)(request->channels + n_channels);
4028 }
4029
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004030 if (n_match_sets) {
4031 if (request->ie)
4032 request->match_sets = (void *)(request->ie + ie_len);
4033 else if (request->ssids)
4034 request->match_sets =
4035 (void *)(request->ssids + n_ssids);
4036 else
4037 request->match_sets =
4038 (void *)(request->channels + n_channels);
4039 }
4040 request->n_match_sets = n_match_sets;
4041
Luciano Coelho807f8a82011-05-11 17:09:35 +03004042 i = 0;
4043 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4044 /* user specified, bail out if channel not found */
4045 nla_for_each_nested(attr,
4046 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
4047 tmp) {
4048 struct ieee80211_channel *chan;
4049
4050 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
4051
4052 if (!chan) {
4053 err = -EINVAL;
4054 goto out_free;
4055 }
4056
4057 /* ignore disabled channels */
4058 if (chan->flags & IEEE80211_CHAN_DISABLED)
4059 continue;
4060
4061 request->channels[i] = chan;
4062 i++;
4063 }
4064 } else {
4065 /* all channels */
4066 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4067 int j;
4068 if (!wiphy->bands[band])
4069 continue;
4070 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
4071 struct ieee80211_channel *chan;
4072
4073 chan = &wiphy->bands[band]->channels[j];
4074
4075 if (chan->flags & IEEE80211_CHAN_DISABLED)
4076 continue;
4077
4078 request->channels[i] = chan;
4079 i++;
4080 }
4081 }
4082 }
4083
4084 if (!i) {
4085 err = -EINVAL;
4086 goto out_free;
4087 }
4088
4089 request->n_channels = i;
4090
4091 i = 0;
4092 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4093 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4094 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004095 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03004096 err = -EINVAL;
4097 goto out_free;
4098 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004099 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004100 memcpy(request->ssids[i].ssid, nla_data(attr),
4101 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03004102 i++;
4103 }
4104 }
4105
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004106 i = 0;
4107 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
4108 nla_for_each_nested(attr,
4109 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4110 tmp) {
4111 struct nlattr *ssid;
4112
4113 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
4114 nla_data(attr), nla_len(attr),
4115 nl80211_match_policy);
4116 ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID];
4117 if (ssid) {
4118 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
4119 err = -EINVAL;
4120 goto out_free;
4121 }
4122 memcpy(request->match_sets[i].ssid.ssid,
4123 nla_data(ssid), nla_len(ssid));
4124 request->match_sets[i].ssid.ssid_len =
4125 nla_len(ssid);
4126 }
4127 i++;
4128 }
4129 }
4130
Luciano Coelho807f8a82011-05-11 17:09:35 +03004131 if (info->attrs[NL80211_ATTR_IE]) {
4132 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4133 memcpy((void *)request->ie,
4134 nla_data(info->attrs[NL80211_ATTR_IE]),
4135 request->ie_len);
4136 }
4137
4138 request->dev = dev;
4139 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004140 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004141
4142 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
4143 if (!err) {
4144 rdev->sched_scan_req = request;
4145 nl80211_send_sched_scan(rdev, dev,
4146 NL80211_CMD_START_SCHED_SCAN);
4147 goto out;
4148 }
4149
4150out_free:
4151 kfree(request);
4152out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004153 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004154 return err;
4155}
4156
4157static int nl80211_stop_sched_scan(struct sk_buff *skb,
4158 struct genl_info *info)
4159{
4160 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004161 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004162
4163 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4164 !rdev->ops->sched_scan_stop)
4165 return -EOPNOTSUPP;
4166
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004167 mutex_lock(&rdev->sched_scan_mtx);
4168 err = __cfg80211_stop_sched_scan(rdev, false);
4169 mutex_unlock(&rdev->sched_scan_mtx);
4170
4171 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004172}
4173
Johannes Berg9720bb32011-06-21 09:45:33 +02004174static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
4175 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004176 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02004177 struct wireless_dev *wdev,
4178 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01004179{
Johannes Berg48ab9052009-07-10 18:42:31 +02004180 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01004181 void *hdr;
4182 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02004183
4184 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004185
Johannes Berg9720bb32011-06-21 09:45:33 +02004186 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004187 NL80211_CMD_NEW_SCAN_RESULTS);
4188 if (!hdr)
4189 return -1;
4190
Johannes Berg9720bb32011-06-21 09:45:33 +02004191 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
4192
Johannes Bergf5ea9122009-08-07 16:17:38 +02004193 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02004194 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01004195
4196 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
4197 if (!bss)
4198 goto nla_put_failure;
4199 if (!is_zero_ether_addr(res->bssid))
4200 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
4201 if (res->information_elements && res->len_information_elements)
4202 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
4203 res->len_information_elements,
4204 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02004205 if (res->beacon_ies && res->len_beacon_ies &&
4206 res->beacon_ies != res->information_elements)
4207 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
4208 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01004209 if (res->tsf)
4210 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
4211 if (res->beacon_interval)
4212 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
4213 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
4214 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02004215 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
4216 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01004217
Johannes Berg77965c92009-02-18 18:45:06 +01004218 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01004219 case CFG80211_SIGNAL_TYPE_MBM:
4220 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
4221 break;
4222 case CFG80211_SIGNAL_TYPE_UNSPEC:
4223 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
4224 break;
4225 default:
4226 break;
4227 }
4228
Johannes Berg48ab9052009-07-10 18:42:31 +02004229 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02004230 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02004231 case NL80211_IFTYPE_STATION:
4232 if (intbss == wdev->current_bss)
4233 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4234 NL80211_BSS_STATUS_ASSOCIATED);
Johannes Berg48ab9052009-07-10 18:42:31 +02004235 break;
4236 case NL80211_IFTYPE_ADHOC:
4237 if (intbss == wdev->current_bss)
4238 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4239 NL80211_BSS_STATUS_IBSS_JOINED);
4240 break;
4241 default:
4242 break;
4243 }
4244
Johannes Berg2a519312009-02-10 21:25:55 +01004245 nla_nest_end(msg, bss);
4246
4247 return genlmsg_end(msg, hdr);
4248
4249 nla_put_failure:
4250 genlmsg_cancel(msg, hdr);
4251 return -EMSGSIZE;
4252}
4253
4254static int nl80211_dump_scan(struct sk_buff *skb,
4255 struct netlink_callback *cb)
4256{
Johannes Berg48ab9052009-07-10 18:42:31 +02004257 struct cfg80211_registered_device *rdev;
4258 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01004259 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02004260 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01004261 int start = cb->args[1], idx = 0;
4262 int err;
4263
Johannes Berg67748892010-10-04 21:14:06 +02004264 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
4265 if (err)
4266 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01004267
Johannes Berg48ab9052009-07-10 18:42:31 +02004268 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01004269
Johannes Berg48ab9052009-07-10 18:42:31 +02004270 wdev_lock(wdev);
4271 spin_lock_bh(&rdev->bss_lock);
4272 cfg80211_bss_expire(rdev);
4273
Johannes Berg9720bb32011-06-21 09:45:33 +02004274 cb->seq = rdev->bss_generation;
4275
Johannes Berg48ab9052009-07-10 18:42:31 +02004276 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01004277 if (++idx <= start)
4278 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02004279 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01004280 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02004281 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004282 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02004283 break;
Johannes Berg2a519312009-02-10 21:25:55 +01004284 }
4285 }
4286
Johannes Berg48ab9052009-07-10 18:42:31 +02004287 spin_unlock_bh(&rdev->bss_lock);
4288 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004289
4290 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02004291 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004292
Johannes Berg67748892010-10-04 21:14:06 +02004293 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01004294}
4295
Holger Schurig61fa7132009-11-11 12:25:40 +01004296static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
4297 int flags, struct net_device *dev,
4298 struct survey_info *survey)
4299{
4300 void *hdr;
4301 struct nlattr *infoattr;
4302
Holger Schurig61fa7132009-11-11 12:25:40 +01004303 hdr = nl80211hdr_put(msg, pid, seq, flags,
4304 NL80211_CMD_NEW_SURVEY_RESULTS);
4305 if (!hdr)
4306 return -ENOMEM;
4307
4308 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
4309
4310 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
4311 if (!infoattr)
4312 goto nla_put_failure;
4313
4314 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
4315 survey->channel->center_freq);
4316 if (survey->filled & SURVEY_INFO_NOISE_DBM)
4317 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
4318 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02004319 if (survey->filled & SURVEY_INFO_IN_USE)
4320 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Felix Fietkau8610c292010-10-09 02:39:29 +02004321 if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
4322 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
4323 survey->channel_time);
4324 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
4325 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
4326 survey->channel_time_busy);
4327 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
4328 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
4329 survey->channel_time_ext_busy);
4330 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
4331 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
4332 survey->channel_time_rx);
4333 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
4334 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
4335 survey->channel_time_tx);
Holger Schurig61fa7132009-11-11 12:25:40 +01004336
4337 nla_nest_end(msg, infoattr);
4338
4339 return genlmsg_end(msg, hdr);
4340
4341 nla_put_failure:
4342 genlmsg_cancel(msg, hdr);
4343 return -EMSGSIZE;
4344}
4345
4346static int nl80211_dump_survey(struct sk_buff *skb,
4347 struct netlink_callback *cb)
4348{
4349 struct survey_info survey;
4350 struct cfg80211_registered_device *dev;
4351 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01004352 int survey_idx = cb->args[1];
4353 int res;
4354
Johannes Berg67748892010-10-04 21:14:06 +02004355 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
4356 if (res)
4357 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01004358
4359 if (!dev->ops->dump_survey) {
4360 res = -EOPNOTSUPP;
4361 goto out_err;
4362 }
4363
4364 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004365 struct ieee80211_channel *chan;
4366
Holger Schurig61fa7132009-11-11 12:25:40 +01004367 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
4368 &survey);
4369 if (res == -ENOENT)
4370 break;
4371 if (res)
4372 goto out_err;
4373
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004374 /* Survey without a channel doesn't make sense */
4375 if (!survey.channel) {
4376 res = -EINVAL;
4377 goto out;
4378 }
4379
4380 chan = ieee80211_get_channel(&dev->wiphy,
4381 survey.channel->center_freq);
4382 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
4383 survey_idx++;
4384 continue;
4385 }
4386
Holger Schurig61fa7132009-11-11 12:25:40 +01004387 if (nl80211_send_survey(skb,
4388 NETLINK_CB(cb->skb).pid,
4389 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4390 netdev,
4391 &survey) < 0)
4392 goto out;
4393 survey_idx++;
4394 }
4395
4396 out:
4397 cb->args[1] = survey_idx;
4398 res = skb->len;
4399 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02004400 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01004401 return res;
4402}
4403
Jouni Malinen255e7372009-03-20 21:21:17 +02004404static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
4405{
Samuel Ortizb23aa672009-07-01 21:26:54 +02004406 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02004407}
4408
Samuel Ortizb23aa672009-07-01 21:26:54 +02004409static bool nl80211_valid_wpa_versions(u32 wpa_versions)
4410{
4411 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
4412 NL80211_WPA_VERSION_2));
4413}
4414
Jouni Malinen636a5d32009-03-19 13:39:22 +02004415static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
4416{
Johannes Berg4c476992010-10-04 21:36:35 +02004417 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4418 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004419 struct ieee80211_channel *chan;
4420 const u8 *bssid, *ssid, *ie = NULL;
4421 int err, ssid_len, ie_len = 0;
4422 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02004423 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004424 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004425
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004426 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4427 return -EINVAL;
4428
4429 if (!info->attrs[NL80211_ATTR_MAC])
4430 return -EINVAL;
4431
Jouni Malinen17780922009-03-27 20:52:47 +02004432 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4433 return -EINVAL;
4434
Johannes Berg19957bb2009-07-02 17:20:43 +02004435 if (!info->attrs[NL80211_ATTR_SSID])
4436 return -EINVAL;
4437
4438 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4439 return -EINVAL;
4440
Johannes Bergfffd0932009-07-08 14:22:54 +02004441 err = nl80211_parse_key(info, &key);
4442 if (err)
4443 return err;
4444
4445 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004446 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4447 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004448 if (!key.p.key || !key.p.key_len)
4449 return -EINVAL;
4450 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4451 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4452 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4453 key.p.key_len != WLAN_KEY_LEN_WEP104))
4454 return -EINVAL;
4455 if (key.idx > 4)
4456 return -EINVAL;
4457 } else {
4458 key.p.key_len = 0;
4459 key.p.key = NULL;
4460 }
4461
Johannes Bergafea0b72010-08-10 09:46:42 +02004462 if (key.idx >= 0) {
4463 int i;
4464 bool ok = false;
4465 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4466 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4467 ok = true;
4468 break;
4469 }
4470 }
Johannes Berg4c476992010-10-04 21:36:35 +02004471 if (!ok)
4472 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004473 }
4474
Johannes Berg4c476992010-10-04 21:36:35 +02004475 if (!rdev->ops->auth)
4476 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004477
Johannes Berg074ac8d2010-09-16 14:58:22 +02004478 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004479 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4480 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004481
Johannes Berg19957bb2009-07-02 17:20:43 +02004482 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004483 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004484 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004485 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4486 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004487
Johannes Berg19957bb2009-07-02 17:20:43 +02004488 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4489 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4490
4491 if (info->attrs[NL80211_ATTR_IE]) {
4492 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4493 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4494 }
4495
4496 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004497 if (!nl80211_valid_auth_type(auth_type))
4498 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004499
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004500 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4501
Johannes Berg95de8172012-01-20 13:55:25 +01004502 /*
4503 * Since we no longer track auth state, ignore
4504 * requests to only change local state.
4505 */
4506 if (local_state_change)
4507 return 0;
4508
Johannes Berg4c476992010-10-04 21:36:35 +02004509 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4510 ssid, ssid_len, ie, ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +01004511 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004512}
4513
Johannes Bergc0692b82010-08-27 14:26:53 +03004514static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4515 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004516 struct cfg80211_crypto_settings *settings,
4517 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004518{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004519 memset(settings, 0, sizeof(*settings));
4520
Samuel Ortizb23aa672009-07-01 21:26:54 +02004521 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4522
Johannes Bergc0692b82010-08-27 14:26:53 +03004523 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4524 u16 proto;
4525 proto = nla_get_u16(
4526 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4527 settings->control_port_ethertype = cpu_to_be16(proto);
4528 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4529 proto != ETH_P_PAE)
4530 return -EINVAL;
4531 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4532 settings->control_port_no_encrypt = true;
4533 } else
4534 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4535
Samuel Ortizb23aa672009-07-01 21:26:54 +02004536 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4537 void *data;
4538 int len, i;
4539
4540 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4541 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4542 settings->n_ciphers_pairwise = len / sizeof(u32);
4543
4544 if (len % sizeof(u32))
4545 return -EINVAL;
4546
Johannes Berg3dc27d22009-07-02 21:36:37 +02004547 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004548 return -EINVAL;
4549
4550 memcpy(settings->ciphers_pairwise, data, len);
4551
4552 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004553 if (!cfg80211_supported_cipher_suite(
4554 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004555 settings->ciphers_pairwise[i]))
4556 return -EINVAL;
4557 }
4558
4559 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4560 settings->cipher_group =
4561 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004562 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
4563 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02004564 return -EINVAL;
4565 }
4566
4567 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4568 settings->wpa_versions =
4569 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4570 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4571 return -EINVAL;
4572 }
4573
4574 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4575 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03004576 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004577
4578 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4579 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4580 settings->n_akm_suites = len / sizeof(u32);
4581
4582 if (len % sizeof(u32))
4583 return -EINVAL;
4584
Jouni Malinen1b9ca022011-09-21 16:13:07 +03004585 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4586 return -EINVAL;
4587
Samuel Ortizb23aa672009-07-01 21:26:54 +02004588 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004589 }
4590
4591 return 0;
4592}
4593
Jouni Malinen636a5d32009-03-19 13:39:22 +02004594static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4595{
Johannes Berg4c476992010-10-04 21:36:35 +02004596 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4597 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004598 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004599 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004600 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004601 int err, ssid_len, ie_len = 0;
4602 bool use_mfp = false;
Ben Greear7e7c8922011-11-18 11:31:59 -08004603 u32 flags = 0;
4604 struct ieee80211_ht_cap *ht_capa = NULL;
4605 struct ieee80211_ht_cap *ht_capa_mask = NULL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004606
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004607 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4608 return -EINVAL;
4609
4610 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004611 !info->attrs[NL80211_ATTR_SSID] ||
4612 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004613 return -EINVAL;
4614
Johannes Berg4c476992010-10-04 21:36:35 +02004615 if (!rdev->ops->assoc)
4616 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004617
Johannes Berg074ac8d2010-09-16 14:58:22 +02004618 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004619 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4620 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004621
Johannes Berg19957bb2009-07-02 17:20:43 +02004622 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004623
Johannes Berg19957bb2009-07-02 17:20:43 +02004624 chan = ieee80211_get_channel(&rdev->wiphy,
4625 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004626 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4627 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004628
Johannes Berg19957bb2009-07-02 17:20:43 +02004629 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4630 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004631
4632 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004633 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4634 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004635 }
4636
Jouni Malinendc6382c2009-05-06 22:09:37 +03004637 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004638 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004639 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004640 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004641 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004642 else if (mfp != NL80211_MFP_NO)
4643 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004644 }
4645
Johannes Berg3e5d7642009-07-07 14:37:26 +02004646 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4647 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4648
Ben Greear7e7c8922011-11-18 11:31:59 -08004649 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
4650 flags |= ASSOC_REQ_DISABLE_HT;
4651
4652 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
4653 ht_capa_mask =
4654 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
4655
4656 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
4657 if (!ht_capa_mask)
4658 return -EINVAL;
4659 ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
4660 }
4661
Johannes Bergc0692b82010-08-27 14:26:53 +03004662 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004663 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004664 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4665 ssid, ssid_len, ie, ie_len, use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -08004666 &crypto, flags, ht_capa,
4667 ht_capa_mask);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004668
Jouni Malinen636a5d32009-03-19 13:39:22 +02004669 return err;
4670}
4671
4672static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4673{
Johannes Berg4c476992010-10-04 21:36:35 +02004674 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4675 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004676 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004677 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004678 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004679 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004680
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004681 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4682 return -EINVAL;
4683
4684 if (!info->attrs[NL80211_ATTR_MAC])
4685 return -EINVAL;
4686
4687 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4688 return -EINVAL;
4689
Johannes Berg4c476992010-10-04 21:36:35 +02004690 if (!rdev->ops->deauth)
4691 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004692
Johannes Berg074ac8d2010-09-16 14:58:22 +02004693 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004694 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4695 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004696
Johannes Berg19957bb2009-07-02 17:20:43 +02004697 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004698
Johannes Berg19957bb2009-07-02 17:20:43 +02004699 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4700 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004701 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004702 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004703 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004704
4705 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004706 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4707 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004708 }
4709
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004710 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4711
Johannes Berg4c476992010-10-04 21:36:35 +02004712 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4713 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004714}
4715
4716static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4717{
Johannes Berg4c476992010-10-04 21:36:35 +02004718 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4719 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004720 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004721 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004722 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004723 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004724
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004725 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4726 return -EINVAL;
4727
4728 if (!info->attrs[NL80211_ATTR_MAC])
4729 return -EINVAL;
4730
4731 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4732 return -EINVAL;
4733
Johannes Berg4c476992010-10-04 21:36:35 +02004734 if (!rdev->ops->disassoc)
4735 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004736
Johannes Berg074ac8d2010-09-16 14:58:22 +02004737 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004738 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4739 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004740
Johannes Berg19957bb2009-07-02 17:20:43 +02004741 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004742
Johannes Berg19957bb2009-07-02 17:20:43 +02004743 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4744 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004745 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004746 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004747 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004748
4749 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004750 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4751 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004752 }
4753
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004754 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4755
Johannes Berg4c476992010-10-04 21:36:35 +02004756 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4757 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004758}
4759
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004760static bool
4761nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4762 int mcast_rate[IEEE80211_NUM_BANDS],
4763 int rateval)
4764{
4765 struct wiphy *wiphy = &rdev->wiphy;
4766 bool found = false;
4767 int band, i;
4768
4769 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4770 struct ieee80211_supported_band *sband;
4771
4772 sband = wiphy->bands[band];
4773 if (!sband)
4774 continue;
4775
4776 for (i = 0; i < sband->n_bitrates; i++) {
4777 if (sband->bitrates[i].bitrate == rateval) {
4778 mcast_rate[band] = i + 1;
4779 found = true;
4780 break;
4781 }
4782 }
4783 }
4784
4785 return found;
4786}
4787
Johannes Berg04a773a2009-04-19 21:24:32 +02004788static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4789{
Johannes Berg4c476992010-10-04 21:36:35 +02004790 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4791 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004792 struct cfg80211_ibss_params ibss;
4793 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004794 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004795 int err;
4796
Johannes Berg8e30bc52009-04-22 17:45:38 +02004797 memset(&ibss, 0, sizeof(ibss));
4798
Johannes Berg04a773a2009-04-19 21:24:32 +02004799 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4800 return -EINVAL;
4801
4802 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4803 !info->attrs[NL80211_ATTR_SSID] ||
4804 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4805 return -EINVAL;
4806
Johannes Berg8e30bc52009-04-22 17:45:38 +02004807 ibss.beacon_interval = 100;
4808
4809 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4810 ibss.beacon_interval =
4811 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4812 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4813 return -EINVAL;
4814 }
4815
Johannes Berg4c476992010-10-04 21:36:35 +02004816 if (!rdev->ops->join_ibss)
4817 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004818
Johannes Berg4c476992010-10-04 21:36:35 +02004819 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4820 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004821
Johannes Berg79c97e92009-07-07 03:56:12 +02004822 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004823
Johannes Berg39193492011-09-16 13:45:25 +02004824 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02004825 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02004826
4827 if (!is_valid_ether_addr(ibss.bssid))
4828 return -EINVAL;
4829 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004830 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4831 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4832
4833 if (info->attrs[NL80211_ATTR_IE]) {
4834 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4835 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4836 }
4837
Alexander Simon54858ee2011-11-30 16:56:32 +01004838 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4839 enum nl80211_channel_type channel_type;
4840
4841 channel_type = nla_get_u32(
4842 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4843 if (channel_type != NL80211_CHAN_NO_HT &&
4844 channel_type != NL80211_CHAN_HT20 &&
4845 channel_type != NL80211_CHAN_HT40MINUS &&
4846 channel_type != NL80211_CHAN_HT40PLUS)
4847 return -EINVAL;
4848
4849 if (channel_type != NL80211_CHAN_NO_HT &&
4850 !(wiphy->features & NL80211_FEATURE_HT_IBSS))
4851 return -EINVAL;
4852
4853 ibss.channel_type = channel_type;
4854 } else {
4855 ibss.channel_type = NL80211_CHAN_NO_HT;
4856 }
4857
4858 ibss.channel = rdev_freq_to_chan(rdev,
4859 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
4860 ibss.channel_type);
Johannes Berg04a773a2009-04-19 21:24:32 +02004861 if (!ibss.channel ||
4862 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004863 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4864 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004865
Alexander Simon54858ee2011-11-30 16:56:32 +01004866 /* Both channels should be able to initiate communication */
4867 if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
4868 ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
4869 !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
4870 ibss.channel_type))
4871 return -EINVAL;
4872
Johannes Berg04a773a2009-04-19 21:24:32 +02004873 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004874 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004875
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004876 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4877 u8 *rates =
4878 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4879 int n_rates =
4880 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4881 struct ieee80211_supported_band *sband =
4882 wiphy->bands[ibss.channel->band];
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004883
Johannes Berg34850ab2011-07-18 18:08:35 +02004884 err = ieee80211_get_ratemask(sband, rates, n_rates,
4885 &ibss.basic_rates);
4886 if (err)
4887 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004888 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004889
4890 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4891 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4892 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4893 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004894
Johannes Berg4c476992010-10-04 21:36:35 +02004895 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4896 connkeys = nl80211_parse_connkeys(rdev,
4897 info->attrs[NL80211_ATTR_KEYS]);
4898 if (IS_ERR(connkeys))
4899 return PTR_ERR(connkeys);
4900 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004901
Antonio Quartulli267335d2012-01-31 20:25:47 +01004902 ibss.control_port =
4903 nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
4904
Johannes Berg4c476992010-10-04 21:36:35 +02004905 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004906 if (err)
4907 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004908 return err;
4909}
4910
4911static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4912{
Johannes Berg4c476992010-10-04 21:36:35 +02004913 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4914 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004915
Johannes Berg4c476992010-10-04 21:36:35 +02004916 if (!rdev->ops->leave_ibss)
4917 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004918
Johannes Berg4c476992010-10-04 21:36:35 +02004919 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4920 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004921
Johannes Berg4c476992010-10-04 21:36:35 +02004922 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004923}
4924
Johannes Bergaff89a92009-07-01 21:26:51 +02004925#ifdef CONFIG_NL80211_TESTMODE
4926static struct genl_multicast_group nl80211_testmode_mcgrp = {
4927 .name = "testmode",
4928};
4929
4930static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4931{
Johannes Berg4c476992010-10-04 21:36:35 +02004932 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02004933 int err;
4934
4935 if (!info->attrs[NL80211_ATTR_TESTDATA])
4936 return -EINVAL;
4937
Johannes Bergaff89a92009-07-01 21:26:51 +02004938 err = -EOPNOTSUPP;
4939 if (rdev->ops->testmode_cmd) {
4940 rdev->testmode_info = info;
4941 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4942 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4943 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4944 rdev->testmode_info = NULL;
4945 }
4946
Johannes Bergaff89a92009-07-01 21:26:51 +02004947 return err;
4948}
4949
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004950static int nl80211_testmode_dump(struct sk_buff *skb,
4951 struct netlink_callback *cb)
4952{
Johannes Berg00918d32011-12-13 17:22:05 +01004953 struct cfg80211_registered_device *rdev;
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004954 int err;
4955 long phy_idx;
4956 void *data = NULL;
4957 int data_len = 0;
4958
4959 if (cb->args[0]) {
4960 /*
4961 * 0 is a valid index, but not valid for args[0],
4962 * so we need to offset by 1.
4963 */
4964 phy_idx = cb->args[0] - 1;
4965 } else {
4966 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
4967 nl80211_fam.attrbuf, nl80211_fam.maxattr,
4968 nl80211_policy);
4969 if (err)
4970 return err;
Johannes Berg00918d32011-12-13 17:22:05 +01004971 if (nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) {
4972 phy_idx = nla_get_u32(
4973 nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
4974 } else {
4975 struct net_device *netdev;
4976
4977 err = get_rdev_dev_by_ifindex(sock_net(skb->sk),
4978 nl80211_fam.attrbuf,
4979 &rdev, &netdev);
4980 if (err)
4981 return err;
4982 dev_put(netdev);
4983 phy_idx = rdev->wiphy_idx;
4984 cfg80211_unlock_rdev(rdev);
4985 }
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004986 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
4987 cb->args[1] =
4988 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
4989 }
4990
4991 if (cb->args[1]) {
4992 data = nla_data((void *)cb->args[1]);
4993 data_len = nla_len((void *)cb->args[1]);
4994 }
4995
4996 mutex_lock(&cfg80211_mutex);
Johannes Berg00918d32011-12-13 17:22:05 +01004997 rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
4998 if (!rdev) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004999 mutex_unlock(&cfg80211_mutex);
5000 return -ENOENT;
5001 }
Johannes Berg00918d32011-12-13 17:22:05 +01005002 cfg80211_lock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005003 mutex_unlock(&cfg80211_mutex);
5004
Johannes Berg00918d32011-12-13 17:22:05 +01005005 if (!rdev->ops->testmode_dump) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005006 err = -EOPNOTSUPP;
5007 goto out_err;
5008 }
5009
5010 while (1) {
5011 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
5012 cb->nlh->nlmsg_seq, NLM_F_MULTI,
5013 NL80211_CMD_TESTMODE);
5014 struct nlattr *tmdata;
5015
Johannes Berg00918d32011-12-13 17:22:05 +01005016 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx) < 0) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005017 genlmsg_cancel(skb, hdr);
5018 break;
5019 }
5020
5021 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5022 if (!tmdata) {
5023 genlmsg_cancel(skb, hdr);
5024 break;
5025 }
Johannes Berg00918d32011-12-13 17:22:05 +01005026 err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb,
5027 data, data_len);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005028 nla_nest_end(skb, tmdata);
5029
5030 if (err == -ENOBUFS || err == -ENOENT) {
5031 genlmsg_cancel(skb, hdr);
5032 break;
5033 } else if (err) {
5034 genlmsg_cancel(skb, hdr);
5035 goto out_err;
5036 }
5037
5038 genlmsg_end(skb, hdr);
5039 }
5040
5041 err = skb->len;
5042 /* see above */
5043 cb->args[0] = phy_idx + 1;
5044 out_err:
Johannes Berg00918d32011-12-13 17:22:05 +01005045 cfg80211_unlock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005046 return err;
5047}
5048
Johannes Bergaff89a92009-07-01 21:26:51 +02005049static struct sk_buff *
5050__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
5051 int approxlen, u32 pid, u32 seq, gfp_t gfp)
5052{
5053 struct sk_buff *skb;
5054 void *hdr;
5055 struct nlattr *data;
5056
5057 skb = nlmsg_new(approxlen + 100, gfp);
5058 if (!skb)
5059 return NULL;
5060
5061 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
5062 if (!hdr) {
5063 kfree_skb(skb);
5064 return NULL;
5065 }
5066
5067 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5068 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5069
5070 ((void **)skb->cb)[0] = rdev;
5071 ((void **)skb->cb)[1] = hdr;
5072 ((void **)skb->cb)[2] = data;
5073
5074 return skb;
5075
5076 nla_put_failure:
5077 kfree_skb(skb);
5078 return NULL;
5079}
5080
5081struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
5082 int approxlen)
5083{
5084 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5085
5086 if (WARN_ON(!rdev->testmode_info))
5087 return NULL;
5088
5089 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
5090 rdev->testmode_info->snd_pid,
5091 rdev->testmode_info->snd_seq,
5092 GFP_KERNEL);
5093}
5094EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
5095
5096int cfg80211_testmode_reply(struct sk_buff *skb)
5097{
5098 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
5099 void *hdr = ((void **)skb->cb)[1];
5100 struct nlattr *data = ((void **)skb->cb)[2];
5101
5102 if (WARN_ON(!rdev->testmode_info)) {
5103 kfree_skb(skb);
5104 return -EINVAL;
5105 }
5106
5107 nla_nest_end(skb, data);
5108 genlmsg_end(skb, hdr);
5109 return genlmsg_reply(skb, rdev->testmode_info);
5110}
5111EXPORT_SYMBOL(cfg80211_testmode_reply);
5112
5113struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
5114 int approxlen, gfp_t gfp)
5115{
5116 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5117
5118 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
5119}
5120EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
5121
5122void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
5123{
5124 void *hdr = ((void **)skb->cb)[1];
5125 struct nlattr *data = ((void **)skb->cb)[2];
5126
5127 nla_nest_end(skb, data);
5128 genlmsg_end(skb, hdr);
5129 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
5130}
5131EXPORT_SYMBOL(cfg80211_testmode_event);
5132#endif
5133
Samuel Ortizb23aa672009-07-01 21:26:54 +02005134static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
5135{
Johannes Berg4c476992010-10-04 21:36:35 +02005136 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5137 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005138 struct cfg80211_connect_params connect;
5139 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02005140 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005141 int err;
5142
5143 memset(&connect, 0, sizeof(connect));
5144
5145 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5146 return -EINVAL;
5147
5148 if (!info->attrs[NL80211_ATTR_SSID] ||
5149 !nla_len(info->attrs[NL80211_ATTR_SSID]))
5150 return -EINVAL;
5151
5152 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
5153 connect.auth_type =
5154 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
5155 if (!nl80211_valid_auth_type(connect.auth_type))
5156 return -EINVAL;
5157 } else
5158 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
5159
5160 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
5161
Johannes Bergc0692b82010-08-27 14:26:53 +03005162 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02005163 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005164 if (err)
5165 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005166
Johannes Berg074ac8d2010-09-16 14:58:22 +02005167 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005168 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5169 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005170
Johannes Berg79c97e92009-07-07 03:56:12 +02005171 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005172
Bala Shanmugam4486ea92012-03-07 17:27:12 +05305173 connect.bg_scan_period = -1;
5174 if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
5175 (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
5176 connect.bg_scan_period =
5177 nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
5178 }
5179
Samuel Ortizb23aa672009-07-01 21:26:54 +02005180 if (info->attrs[NL80211_ATTR_MAC])
5181 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5182 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5183 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5184
5185 if (info->attrs[NL80211_ATTR_IE]) {
5186 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5187 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5188 }
5189
5190 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
5191 connect.channel =
5192 ieee80211_get_channel(wiphy,
5193 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
5194 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02005195 connect.channel->flags & IEEE80211_CHAN_DISABLED)
5196 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005197 }
5198
Johannes Bergfffd0932009-07-08 14:22:54 +02005199 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5200 connkeys = nl80211_parse_connkeys(rdev,
5201 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02005202 if (IS_ERR(connkeys))
5203 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005204 }
5205
Ben Greear7e7c8922011-11-18 11:31:59 -08005206 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
5207 connect.flags |= ASSOC_REQ_DISABLE_HT;
5208
5209 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5210 memcpy(&connect.ht_capa_mask,
5211 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
5212 sizeof(connect.ht_capa_mask));
5213
5214 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
5215 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5216 return -EINVAL;
5217 memcpy(&connect.ht_capa,
5218 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
5219 sizeof(connect.ht_capa));
5220 }
5221
Johannes Bergfffd0932009-07-08 14:22:54 +02005222 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005223 if (err)
5224 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005225 return err;
5226}
5227
5228static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
5229{
Johannes Berg4c476992010-10-04 21:36:35 +02005230 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5231 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005232 u16 reason;
5233
5234 if (!info->attrs[NL80211_ATTR_REASON_CODE])
5235 reason = WLAN_REASON_DEAUTH_LEAVING;
5236 else
5237 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5238
5239 if (reason == 0)
5240 return -EINVAL;
5241
Johannes Berg074ac8d2010-09-16 14:58:22 +02005242 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005243 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5244 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005245
Johannes Berg4c476992010-10-04 21:36:35 +02005246 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005247}
5248
Johannes Berg463d0182009-07-14 00:33:35 +02005249static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
5250{
Johannes Berg4c476992010-10-04 21:36:35 +02005251 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02005252 struct net *net;
5253 int err;
5254 u32 pid;
5255
5256 if (!info->attrs[NL80211_ATTR_PID])
5257 return -EINVAL;
5258
5259 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
5260
Johannes Berg463d0182009-07-14 00:33:35 +02005261 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02005262 if (IS_ERR(net))
5263 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005264
5265 err = 0;
5266
5267 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02005268 if (!net_eq(wiphy_net(&rdev->wiphy), net))
5269 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02005270
Johannes Berg463d0182009-07-14 00:33:35 +02005271 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005272 return err;
5273}
5274
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005275static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
5276{
Johannes Berg4c476992010-10-04 21:36:35 +02005277 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005278 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
5279 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02005280 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005281 struct cfg80211_pmksa pmksa;
5282
5283 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
5284
5285 if (!info->attrs[NL80211_ATTR_MAC])
5286 return -EINVAL;
5287
5288 if (!info->attrs[NL80211_ATTR_PMKID])
5289 return -EINVAL;
5290
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005291 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
5292 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5293
Johannes Berg074ac8d2010-09-16 14:58:22 +02005294 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005295 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5296 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005297
5298 switch (info->genlhdr->cmd) {
5299 case NL80211_CMD_SET_PMKSA:
5300 rdev_ops = rdev->ops->set_pmksa;
5301 break;
5302 case NL80211_CMD_DEL_PMKSA:
5303 rdev_ops = rdev->ops->del_pmksa;
5304 break;
5305 default:
5306 WARN_ON(1);
5307 break;
5308 }
5309
Johannes Berg4c476992010-10-04 21:36:35 +02005310 if (!rdev_ops)
5311 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005312
Johannes Berg4c476992010-10-04 21:36:35 +02005313 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005314}
5315
5316static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
5317{
Johannes Berg4c476992010-10-04 21:36:35 +02005318 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5319 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005320
Johannes Berg074ac8d2010-09-16 14:58:22 +02005321 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005322 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5323 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005324
Johannes Berg4c476992010-10-04 21:36:35 +02005325 if (!rdev->ops->flush_pmksa)
5326 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005327
Johannes Berg4c476992010-10-04 21:36:35 +02005328 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005329}
5330
Arik Nemtsov109086c2011-09-28 14:12:50 +03005331static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
5332{
5333 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5334 struct net_device *dev = info->user_ptr[1];
5335 u8 action_code, dialog_token;
5336 u16 status_code;
5337 u8 *peer;
5338
5339 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5340 !rdev->ops->tdls_mgmt)
5341 return -EOPNOTSUPP;
5342
5343 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
5344 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
5345 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
5346 !info->attrs[NL80211_ATTR_IE] ||
5347 !info->attrs[NL80211_ATTR_MAC])
5348 return -EINVAL;
5349
5350 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5351 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
5352 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
5353 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
5354
5355 return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
5356 dialog_token, status_code,
5357 nla_data(info->attrs[NL80211_ATTR_IE]),
5358 nla_len(info->attrs[NL80211_ATTR_IE]));
5359}
5360
5361static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
5362{
5363 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5364 struct net_device *dev = info->user_ptr[1];
5365 enum nl80211_tdls_operation operation;
5366 u8 *peer;
5367
5368 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5369 !rdev->ops->tdls_oper)
5370 return -EOPNOTSUPP;
5371
5372 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
5373 !info->attrs[NL80211_ATTR_MAC])
5374 return -EINVAL;
5375
5376 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
5377 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5378
5379 return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
5380}
5381
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005382static int nl80211_remain_on_channel(struct sk_buff *skb,
5383 struct genl_info *info)
5384{
Johannes Berg4c476992010-10-04 21:36:35 +02005385 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5386 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005387 struct ieee80211_channel *chan;
5388 struct sk_buff *msg;
5389 void *hdr;
5390 u64 cookie;
5391 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
5392 u32 freq, duration;
5393 int err;
5394
5395 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5396 !info->attrs[NL80211_ATTR_DURATION])
5397 return -EINVAL;
5398
5399 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5400
5401 /*
5402 * We should be on that channel for at least one jiffie,
5403 * and more than 5 seconds seems excessive.
5404 */
Johannes Berga2939112010-12-14 17:54:28 +01005405 if (!duration || !msecs_to_jiffies(duration) ||
5406 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005407 return -EINVAL;
5408
Johannes Berg7c4ef712011-11-18 15:33:48 +01005409 if (!rdev->ops->remain_on_channel ||
5410 !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
Johannes Berg4c476992010-10-04 21:36:35 +02005411 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005412
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005413 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5414 channel_type = nla_get_u32(
5415 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5416 if (channel_type != NL80211_CHAN_NO_HT &&
5417 channel_type != NL80211_CHAN_HT20 &&
5418 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005419 channel_type != NL80211_CHAN_HT40MINUS)
5420 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005421 }
5422
5423 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5424 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005425 if (chan == NULL)
5426 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005427
5428 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005429 if (!msg)
5430 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005431
5432 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5433 NL80211_CMD_REMAIN_ON_CHANNEL);
5434
5435 if (IS_ERR(hdr)) {
5436 err = PTR_ERR(hdr);
5437 goto free_msg;
5438 }
5439
5440 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
5441 channel_type, duration, &cookie);
5442
5443 if (err)
5444 goto free_msg;
5445
5446 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5447
5448 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005449
5450 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005451
5452 nla_put_failure:
5453 err = -ENOBUFS;
5454 free_msg:
5455 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005456 return err;
5457}
5458
5459static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
5460 struct genl_info *info)
5461{
Johannes Berg4c476992010-10-04 21:36:35 +02005462 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5463 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005464 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005465
5466 if (!info->attrs[NL80211_ATTR_COOKIE])
5467 return -EINVAL;
5468
Johannes Berg4c476992010-10-04 21:36:35 +02005469 if (!rdev->ops->cancel_remain_on_channel)
5470 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005471
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005472 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5473
Johannes Berg4c476992010-10-04 21:36:35 +02005474 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005475}
5476
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005477static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
5478 u8 *rates, u8 rates_len)
5479{
5480 u8 i;
5481 u32 mask = 0;
5482
5483 for (i = 0; i < rates_len; i++) {
5484 int rate = (rates[i] & 0x7f) * 5;
5485 int ridx;
5486 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
5487 struct ieee80211_rate *srate =
5488 &sband->bitrates[ridx];
5489 if (rate == srate->bitrate) {
5490 mask |= 1 << ridx;
5491 break;
5492 }
5493 }
5494 if (ridx == sband->n_bitrates)
5495 return 0; /* rate not found */
5496 }
5497
5498 return mask;
5499}
5500
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005501static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
5502 u8 *rates, u8 rates_len,
5503 u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
5504{
5505 u8 i;
5506
5507 memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
5508
5509 for (i = 0; i < rates_len; i++) {
5510 int ridx, rbit;
5511
5512 ridx = rates[i] / 8;
5513 rbit = BIT(rates[i] % 8);
5514
5515 /* check validity */
Dan Carpenter910570b2012-02-01 10:42:11 +03005516 if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005517 return false;
5518
5519 /* check availability */
5520 if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
5521 mcs[ridx] |= rbit;
5522 else
5523 return false;
5524 }
5525
5526 return true;
5527}
5528
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00005529static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005530 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
5531 .len = NL80211_MAX_SUPP_RATES },
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005532 [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
5533 .len = NL80211_MAX_SUPP_HT_RATES },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005534};
5535
5536static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
5537 struct genl_info *info)
5538{
5539 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02005540 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005541 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02005542 int rem, i;
5543 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005544 struct nlattr *tx_rates;
5545 struct ieee80211_supported_band *sband;
5546
5547 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
5548 return -EINVAL;
5549
Johannes Berg4c476992010-10-04 21:36:35 +02005550 if (!rdev->ops->set_bitrate_mask)
5551 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005552
5553 memset(&mask, 0, sizeof(mask));
5554 /* Default to all rates enabled */
5555 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
5556 sband = rdev->wiphy.bands[i];
5557 mask.control[i].legacy =
5558 sband ? (1 << sband->n_bitrates) - 1 : 0;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005559 if (sband)
5560 memcpy(mask.control[i].mcs,
5561 sband->ht_cap.mcs.rx_mask,
5562 sizeof(mask.control[i].mcs));
5563 else
5564 memset(mask.control[i].mcs, 0,
5565 sizeof(mask.control[i].mcs));
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005566 }
5567
5568 /*
5569 * The nested attribute uses enum nl80211_band as the index. This maps
5570 * directly to the enum ieee80211_band values used in cfg80211.
5571 */
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005572 BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005573 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
5574 {
5575 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02005576 if (band < 0 || band >= IEEE80211_NUM_BANDS)
5577 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005578 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02005579 if (sband == NULL)
5580 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005581 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
5582 nla_len(tx_rates), nl80211_txattr_policy);
5583 if (tb[NL80211_TXRATE_LEGACY]) {
5584 mask.control[band].legacy = rateset_to_mask(
5585 sband,
5586 nla_data(tb[NL80211_TXRATE_LEGACY]),
5587 nla_len(tb[NL80211_TXRATE_LEGACY]));
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005588 }
5589 if (tb[NL80211_TXRATE_MCS]) {
5590 if (!ht_rateset_to_mask(
5591 sband,
5592 nla_data(tb[NL80211_TXRATE_MCS]),
5593 nla_len(tb[NL80211_TXRATE_MCS]),
5594 mask.control[band].mcs))
5595 return -EINVAL;
5596 }
5597
5598 if (mask.control[band].legacy == 0) {
5599 /* don't allow empty legacy rates if HT
5600 * is not even supported. */
5601 if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
5602 return -EINVAL;
5603
5604 for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
5605 if (mask.control[band].mcs[i])
5606 break;
5607
5608 /* legacy and mcs rates may not be both empty */
5609 if (i == IEEE80211_HT_MCS_MASK_LEN)
Johannes Berg4c476992010-10-04 21:36:35 +02005610 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005611 }
5612 }
5613
Johannes Berg4c476992010-10-04 21:36:35 +02005614 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005615}
5616
Johannes Berg2e161f72010-08-12 15:38:38 +02005617static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005618{
Johannes Berg4c476992010-10-04 21:36:35 +02005619 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5620 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02005621 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02005622
5623 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
5624 return -EINVAL;
5625
Johannes Berg2e161f72010-08-12 15:38:38 +02005626 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
5627 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02005628
Johannes Berg9d38d852010-06-09 17:20:33 +02005629 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005630 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005631 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5632 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5633 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005634 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005635 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5636 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005637
5638 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005639 if (!rdev->ops->mgmt_tx)
5640 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005641
Johannes Berg4c476992010-10-04 21:36:35 +02005642 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02005643 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005644 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5645 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005646}
5647
Johannes Berg2e161f72010-08-12 15:38:38 +02005648static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005649{
Johannes Berg4c476992010-10-04 21:36:35 +02005650 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5651 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005652 struct ieee80211_channel *chan;
5653 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005654 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005655 u32 freq;
5656 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01005657 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005658 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01005659 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005660 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01005661 bool offchan, no_cck, dont_wait_for_ack;
5662
5663 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02005664
5665 if (!info->attrs[NL80211_ATTR_FRAME] ||
5666 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5667 return -EINVAL;
5668
Johannes Berg4c476992010-10-04 21:36:35 +02005669 if (!rdev->ops->mgmt_tx)
5670 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005671
Johannes Berg9d38d852010-06-09 17:20:33 +02005672 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005673 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005674 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5675 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5676 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005677 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005678 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5679 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005680
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005681 if (info->attrs[NL80211_ATTR_DURATION]) {
Johannes Berg7c4ef712011-11-18 15:33:48 +01005682 if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005683 return -EINVAL;
5684 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5685 }
5686
Jouni Malinen026331c2010-02-15 12:53:10 +02005687 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5688 channel_type = nla_get_u32(
5689 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5690 if (channel_type != NL80211_CHAN_NO_HT &&
5691 channel_type != NL80211_CHAN_HT20 &&
5692 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005693 channel_type != NL80211_CHAN_HT40MINUS)
5694 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005695 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005696 }
5697
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005698 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5699
Johannes Berg7c4ef712011-11-18 15:33:48 +01005700 if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
5701 return -EINVAL;
5702
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305703 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5704
Jouni Malinen026331c2010-02-15 12:53:10 +02005705 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5706 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005707 if (chan == NULL)
5708 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005709
Johannes Berge247bd902011-11-04 11:18:21 +01005710 if (!dont_wait_for_ack) {
5711 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5712 if (!msg)
5713 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005714
Johannes Berge247bd902011-11-04 11:18:21 +01005715 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5716 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005717
Johannes Berge247bd902011-11-04 11:18:21 +01005718 if (IS_ERR(hdr)) {
5719 err = PTR_ERR(hdr);
5720 goto free_msg;
5721 }
Jouni Malinen026331c2010-02-15 12:53:10 +02005722 }
Johannes Berge247bd902011-11-04 11:18:21 +01005723
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005724 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5725 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02005726 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5727 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01005728 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005729 if (err)
5730 goto free_msg;
5731
Johannes Berge247bd902011-11-04 11:18:21 +01005732 if (msg) {
5733 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005734
Johannes Berge247bd902011-11-04 11:18:21 +01005735 genlmsg_end(msg, hdr);
5736 return genlmsg_reply(msg, info);
5737 }
5738
5739 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02005740
5741 nla_put_failure:
5742 err = -ENOBUFS;
5743 free_msg:
5744 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005745 return err;
5746}
5747
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005748static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5749{
5750 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5751 struct net_device *dev = info->user_ptr[1];
5752 u64 cookie;
5753
5754 if (!info->attrs[NL80211_ATTR_COOKIE])
5755 return -EINVAL;
5756
5757 if (!rdev->ops->mgmt_tx_cancel_wait)
5758 return -EOPNOTSUPP;
5759
5760 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5761 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5762 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5763 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5764 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5765 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5766 return -EOPNOTSUPP;
5767
5768 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5769
5770 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5771}
5772
Kalle Valoffb9eb32010-02-17 17:58:10 +02005773static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5774{
Johannes Berg4c476992010-10-04 21:36:35 +02005775 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005776 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005777 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005778 u8 ps_state;
5779 bool state;
5780 int err;
5781
Johannes Berg4c476992010-10-04 21:36:35 +02005782 if (!info->attrs[NL80211_ATTR_PS_STATE])
5783 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005784
5785 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5786
Johannes Berg4c476992010-10-04 21:36:35 +02005787 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5788 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005789
5790 wdev = dev->ieee80211_ptr;
5791
Johannes Berg4c476992010-10-04 21:36:35 +02005792 if (!rdev->ops->set_power_mgmt)
5793 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005794
5795 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5796
5797 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005798 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005799
Johannes Berg4c476992010-10-04 21:36:35 +02005800 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5801 wdev->ps_timeout);
5802 if (!err)
5803 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005804 return err;
5805}
5806
5807static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5808{
Johannes Berg4c476992010-10-04 21:36:35 +02005809 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005810 enum nl80211_ps_state ps_state;
5811 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005812 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005813 struct sk_buff *msg;
5814 void *hdr;
5815 int err;
5816
Kalle Valoffb9eb32010-02-17 17:58:10 +02005817 wdev = dev->ieee80211_ptr;
5818
Johannes Berg4c476992010-10-04 21:36:35 +02005819 if (!rdev->ops->set_power_mgmt)
5820 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005821
5822 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005823 if (!msg)
5824 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005825
5826 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5827 NL80211_CMD_GET_POWER_SAVE);
5828 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005829 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005830 goto free_msg;
5831 }
5832
5833 if (wdev->ps)
5834 ps_state = NL80211_PS_ENABLED;
5835 else
5836 ps_state = NL80211_PS_DISABLED;
5837
5838 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5839
5840 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005841 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005842
Johannes Berg4c476992010-10-04 21:36:35 +02005843 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005844 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005845 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005846 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005847 return err;
5848}
5849
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005850static struct nla_policy
5851nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5852 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5853 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5854 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5855};
5856
5857static int nl80211_set_cqm_rssi(struct genl_info *info,
5858 s32 threshold, u32 hysteresis)
5859{
Johannes Berg4c476992010-10-04 21:36:35 +02005860 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005861 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005862 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005863
5864 if (threshold > 0)
5865 return -EINVAL;
5866
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005867 wdev = dev->ieee80211_ptr;
5868
Johannes Berg4c476992010-10-04 21:36:35 +02005869 if (!rdev->ops->set_cqm_rssi_config)
5870 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005871
Johannes Berg074ac8d2010-09-16 14:58:22 +02005872 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005873 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
5874 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005875
Johannes Berg4c476992010-10-04 21:36:35 +02005876 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5877 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005878}
5879
5880static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5881{
5882 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5883 struct nlattr *cqm;
5884 int err;
5885
5886 cqm = info->attrs[NL80211_ATTR_CQM];
5887 if (!cqm) {
5888 err = -EINVAL;
5889 goto out;
5890 }
5891
5892 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5893 nl80211_attr_cqm_policy);
5894 if (err)
5895 goto out;
5896
5897 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5898 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5899 s32 threshold;
5900 u32 hysteresis;
5901 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5902 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5903 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5904 } else
5905 err = -EINVAL;
5906
5907out:
5908 return err;
5909}
5910
Johannes Berg29cbe682010-12-03 09:20:44 +01005911static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
5912{
5913 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5914 struct net_device *dev = info->user_ptr[1];
5915 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08005916 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01005917 int err;
5918
5919 /* start with default */
5920 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08005921 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01005922
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005923 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01005924 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005925 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01005926 if (err)
5927 return err;
5928 }
5929
5930 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
5931 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
5932 return -EINVAL;
5933
Javier Cardonac80d5452010-12-16 17:37:49 -08005934 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
5935 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
5936
Chun-Yeow Yeoh4bb62342011-11-24 17:15:20 -08005937 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
5938 !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
5939 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
5940 return -EINVAL;
5941
Javier Cardonac80d5452010-12-16 17:37:49 -08005942 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
5943 /* parse additional setup parameters if given */
5944 err = nl80211_parse_mesh_setup(info, &setup);
5945 if (err)
5946 return err;
5947 }
5948
5949 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01005950}
5951
5952static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
5953{
5954 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5955 struct net_device *dev = info->user_ptr[1];
5956
5957 return cfg80211_leave_mesh(rdev, dev);
5958}
5959
Johannes Bergff1b6e62011-05-04 15:37:28 +02005960static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
5961{
5962 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5963 struct sk_buff *msg;
5964 void *hdr;
5965
5966 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5967 return -EOPNOTSUPP;
5968
5969 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5970 if (!msg)
5971 return -ENOMEM;
5972
5973 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5974 NL80211_CMD_GET_WOWLAN);
5975 if (!hdr)
5976 goto nla_put_failure;
5977
5978 if (rdev->wowlan) {
5979 struct nlattr *nl_wowlan;
5980
5981 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
5982 if (!nl_wowlan)
5983 goto nla_put_failure;
5984
5985 if (rdev->wowlan->any)
5986 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
5987 if (rdev->wowlan->disconnect)
5988 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
5989 if (rdev->wowlan->magic_pkt)
5990 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02005991 if (rdev->wowlan->gtk_rekey_failure)
5992 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
5993 if (rdev->wowlan->eap_identity_req)
5994 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
5995 if (rdev->wowlan->four_way_handshake)
5996 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
5997 if (rdev->wowlan->rfkill_release)
5998 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02005999 if (rdev->wowlan->n_patterns) {
6000 struct nlattr *nl_pats, *nl_pat;
6001 int i, pat_len;
6002
6003 nl_pats = nla_nest_start(msg,
6004 NL80211_WOWLAN_TRIG_PKT_PATTERN);
6005 if (!nl_pats)
6006 goto nla_put_failure;
6007
6008 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
6009 nl_pat = nla_nest_start(msg, i + 1);
6010 if (!nl_pat)
6011 goto nla_put_failure;
6012 pat_len = rdev->wowlan->patterns[i].pattern_len;
6013 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
6014 DIV_ROUND_UP(pat_len, 8),
6015 rdev->wowlan->patterns[i].mask);
6016 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
6017 pat_len,
6018 rdev->wowlan->patterns[i].pattern);
6019 nla_nest_end(msg, nl_pat);
6020 }
6021 nla_nest_end(msg, nl_pats);
6022 }
6023
6024 nla_nest_end(msg, nl_wowlan);
6025 }
6026
6027 genlmsg_end(msg, hdr);
6028 return genlmsg_reply(msg, info);
6029
6030nla_put_failure:
6031 nlmsg_free(msg);
6032 return -ENOBUFS;
6033}
6034
6035static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
6036{
6037 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6038 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
6039 struct cfg80211_wowlan no_triggers = {};
6040 struct cfg80211_wowlan new_triggers = {};
6041 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
6042 int err, i;
6043
6044 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6045 return -EOPNOTSUPP;
6046
6047 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
6048 goto no_triggers;
6049
6050 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
6051 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6052 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6053 nl80211_wowlan_policy);
6054 if (err)
6055 return err;
6056
6057 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
6058 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
6059 return -EINVAL;
6060 new_triggers.any = true;
6061 }
6062
6063 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
6064 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
6065 return -EINVAL;
6066 new_triggers.disconnect = true;
6067 }
6068
6069 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
6070 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
6071 return -EINVAL;
6072 new_triggers.magic_pkt = true;
6073 }
6074
Johannes Berg77dbbb12011-07-13 10:48:55 +02006075 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
6076 return -EINVAL;
6077
6078 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
6079 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
6080 return -EINVAL;
6081 new_triggers.gtk_rekey_failure = true;
6082 }
6083
6084 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
6085 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
6086 return -EINVAL;
6087 new_triggers.eap_identity_req = true;
6088 }
6089
6090 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
6091 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
6092 return -EINVAL;
6093 new_triggers.four_way_handshake = true;
6094 }
6095
6096 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
6097 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
6098 return -EINVAL;
6099 new_triggers.rfkill_release = true;
6100 }
6101
Johannes Bergff1b6e62011-05-04 15:37:28 +02006102 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
6103 struct nlattr *pat;
6104 int n_patterns = 0;
6105 int rem, pat_len, mask_len;
6106 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
6107
6108 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6109 rem)
6110 n_patterns++;
6111 if (n_patterns > wowlan->n_patterns)
6112 return -EINVAL;
6113
6114 new_triggers.patterns = kcalloc(n_patterns,
6115 sizeof(new_triggers.patterns[0]),
6116 GFP_KERNEL);
6117 if (!new_triggers.patterns)
6118 return -ENOMEM;
6119
6120 new_triggers.n_patterns = n_patterns;
6121 i = 0;
6122
6123 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6124 rem) {
6125 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
6126 nla_data(pat), nla_len(pat), NULL);
6127 err = -EINVAL;
6128 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
6129 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
6130 goto error;
6131 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
6132 mask_len = DIV_ROUND_UP(pat_len, 8);
6133 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
6134 mask_len)
6135 goto error;
6136 if (pat_len > wowlan->pattern_max_len ||
6137 pat_len < wowlan->pattern_min_len)
6138 goto error;
6139
6140 new_triggers.patterns[i].mask =
6141 kmalloc(mask_len + pat_len, GFP_KERNEL);
6142 if (!new_triggers.patterns[i].mask) {
6143 err = -ENOMEM;
6144 goto error;
6145 }
6146 new_triggers.patterns[i].pattern =
6147 new_triggers.patterns[i].mask + mask_len;
6148 memcpy(new_triggers.patterns[i].mask,
6149 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
6150 mask_len);
6151 new_triggers.patterns[i].pattern_len = pat_len;
6152 memcpy(new_triggers.patterns[i].pattern,
6153 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
6154 pat_len);
6155 i++;
6156 }
6157 }
6158
6159 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
6160 struct cfg80211_wowlan *ntrig;
6161 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
6162 GFP_KERNEL);
6163 if (!ntrig) {
6164 err = -ENOMEM;
6165 goto error;
6166 }
6167 cfg80211_rdev_free_wowlan(rdev);
6168 rdev->wowlan = ntrig;
6169 } else {
6170 no_triggers:
6171 cfg80211_rdev_free_wowlan(rdev);
6172 rdev->wowlan = NULL;
6173 }
6174
6175 return 0;
6176 error:
6177 for (i = 0; i < new_triggers.n_patterns; i++)
6178 kfree(new_triggers.patterns[i].mask);
6179 kfree(new_triggers.patterns);
6180 return err;
6181}
6182
Johannes Berge5497d72011-07-05 16:35:40 +02006183static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
6184{
6185 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6186 struct net_device *dev = info->user_ptr[1];
6187 struct wireless_dev *wdev = dev->ieee80211_ptr;
6188 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
6189 struct cfg80211_gtk_rekey_data rekey_data;
6190 int err;
6191
6192 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
6193 return -EINVAL;
6194
6195 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
6196 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
6197 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
6198 nl80211_rekey_policy);
6199 if (err)
6200 return err;
6201
6202 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
6203 return -ERANGE;
6204 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
6205 return -ERANGE;
6206 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
6207 return -ERANGE;
6208
6209 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
6210 NL80211_KEK_LEN);
6211 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
6212 NL80211_KCK_LEN);
6213 memcpy(rekey_data.replay_ctr,
6214 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
6215 NL80211_REPLAY_CTR_LEN);
6216
6217 wdev_lock(wdev);
6218 if (!wdev->current_bss) {
6219 err = -ENOTCONN;
6220 goto out;
6221 }
6222
6223 if (!rdev->ops->set_rekey_data) {
6224 err = -EOPNOTSUPP;
6225 goto out;
6226 }
6227
6228 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
6229 out:
6230 wdev_unlock(wdev);
6231 return err;
6232}
6233
Johannes Berg28946da2011-11-04 11:18:12 +01006234static int nl80211_register_unexpected_frame(struct sk_buff *skb,
6235 struct genl_info *info)
6236{
6237 struct net_device *dev = info->user_ptr[1];
6238 struct wireless_dev *wdev = dev->ieee80211_ptr;
6239
6240 if (wdev->iftype != NL80211_IFTYPE_AP &&
6241 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6242 return -EINVAL;
6243
6244 if (wdev->ap_unexpected_nlpid)
6245 return -EBUSY;
6246
6247 wdev->ap_unexpected_nlpid = info->snd_pid;
6248 return 0;
6249}
6250
Johannes Berg7f6cf312011-11-04 11:18:15 +01006251static int nl80211_probe_client(struct sk_buff *skb,
6252 struct genl_info *info)
6253{
6254 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6255 struct net_device *dev = info->user_ptr[1];
6256 struct wireless_dev *wdev = dev->ieee80211_ptr;
6257 struct sk_buff *msg;
6258 void *hdr;
6259 const u8 *addr;
6260 u64 cookie;
6261 int err;
6262
6263 if (wdev->iftype != NL80211_IFTYPE_AP &&
6264 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6265 return -EOPNOTSUPP;
6266
6267 if (!info->attrs[NL80211_ATTR_MAC])
6268 return -EINVAL;
6269
6270 if (!rdev->ops->probe_client)
6271 return -EOPNOTSUPP;
6272
6273 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6274 if (!msg)
6275 return -ENOMEM;
6276
6277 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6278 NL80211_CMD_PROBE_CLIENT);
6279
6280 if (IS_ERR(hdr)) {
6281 err = PTR_ERR(hdr);
6282 goto free_msg;
6283 }
6284
6285 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
6286
6287 err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
6288 if (err)
6289 goto free_msg;
6290
6291 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6292
6293 genlmsg_end(msg, hdr);
6294
6295 return genlmsg_reply(msg, info);
6296
6297 nla_put_failure:
6298 err = -ENOBUFS;
6299 free_msg:
6300 nlmsg_free(msg);
6301 return err;
6302}
6303
Johannes Berg5e760232011-11-04 11:18:17 +01006304static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
6305{
6306 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6307
6308 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
6309 return -EOPNOTSUPP;
6310
6311 if (rdev->ap_beacons_nlpid)
6312 return -EBUSY;
6313
6314 rdev->ap_beacons_nlpid = info->snd_pid;
6315
6316 return 0;
6317}
6318
Johannes Berg4c476992010-10-04 21:36:35 +02006319#define NL80211_FLAG_NEED_WIPHY 0x01
6320#define NL80211_FLAG_NEED_NETDEV 0x02
6321#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02006322#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
6323#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
6324 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02006325
6326static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
6327 struct genl_info *info)
6328{
6329 struct cfg80211_registered_device *rdev;
6330 struct net_device *dev;
6331 int err;
6332 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
6333
6334 if (rtnl)
6335 rtnl_lock();
6336
6337 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
6338 rdev = cfg80211_get_dev_from_info(info);
6339 if (IS_ERR(rdev)) {
6340 if (rtnl)
6341 rtnl_unlock();
6342 return PTR_ERR(rdev);
6343 }
6344 info->user_ptr[0] = rdev;
6345 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
Johannes Berg00918d32011-12-13 17:22:05 +01006346 err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs,
6347 &rdev, &dev);
Johannes Berg4c476992010-10-04 21:36:35 +02006348 if (err) {
6349 if (rtnl)
6350 rtnl_unlock();
6351 return err;
6352 }
Johannes Berg41265712010-10-04 21:14:05 +02006353 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
6354 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02006355 cfg80211_unlock_rdev(rdev);
6356 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02006357 if (rtnl)
6358 rtnl_unlock();
6359 return -ENETDOWN;
6360 }
Johannes Berg4c476992010-10-04 21:36:35 +02006361 info->user_ptr[0] = rdev;
6362 info->user_ptr[1] = dev;
6363 }
6364
6365 return 0;
6366}
6367
6368static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
6369 struct genl_info *info)
6370{
6371 if (info->user_ptr[0])
6372 cfg80211_unlock_rdev(info->user_ptr[0]);
6373 if (info->user_ptr[1])
6374 dev_put(info->user_ptr[1]);
6375 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
6376 rtnl_unlock();
6377}
6378
Johannes Berg55682962007-09-20 13:09:35 -04006379static struct genl_ops nl80211_ops[] = {
6380 {
6381 .cmd = NL80211_CMD_GET_WIPHY,
6382 .doit = nl80211_get_wiphy,
6383 .dumpit = nl80211_dump_wiphy,
6384 .policy = nl80211_policy,
6385 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006386 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04006387 },
6388 {
6389 .cmd = NL80211_CMD_SET_WIPHY,
6390 .doit = nl80211_set_wiphy,
6391 .policy = nl80211_policy,
6392 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006393 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006394 },
6395 {
6396 .cmd = NL80211_CMD_GET_INTERFACE,
6397 .doit = nl80211_get_interface,
6398 .dumpit = nl80211_dump_interface,
6399 .policy = nl80211_policy,
6400 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006401 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04006402 },
6403 {
6404 .cmd = NL80211_CMD_SET_INTERFACE,
6405 .doit = nl80211_set_interface,
6406 .policy = nl80211_policy,
6407 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006408 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6409 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006410 },
6411 {
6412 .cmd = NL80211_CMD_NEW_INTERFACE,
6413 .doit = nl80211_new_interface,
6414 .policy = nl80211_policy,
6415 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006416 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6417 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006418 },
6419 {
6420 .cmd = NL80211_CMD_DEL_INTERFACE,
6421 .doit = nl80211_del_interface,
6422 .policy = nl80211_policy,
6423 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006424 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6425 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006426 },
Johannes Berg41ade002007-12-19 02:03:29 +01006427 {
6428 .cmd = NL80211_CMD_GET_KEY,
6429 .doit = nl80211_get_key,
6430 .policy = nl80211_policy,
6431 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006432 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006433 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006434 },
6435 {
6436 .cmd = NL80211_CMD_SET_KEY,
6437 .doit = nl80211_set_key,
6438 .policy = nl80211_policy,
6439 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006440 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006441 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006442 },
6443 {
6444 .cmd = NL80211_CMD_NEW_KEY,
6445 .doit = nl80211_new_key,
6446 .policy = nl80211_policy,
6447 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006448 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006449 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006450 },
6451 {
6452 .cmd = NL80211_CMD_DEL_KEY,
6453 .doit = nl80211_del_key,
6454 .policy = nl80211_policy,
6455 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006456 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006457 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006458 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01006459 {
6460 .cmd = NL80211_CMD_SET_BEACON,
6461 .policy = nl80211_policy,
6462 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006463 .doit = nl80211_set_beacon,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006464 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006465 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006466 },
6467 {
Johannes Berg88600202012-02-13 15:17:18 +01006468 .cmd = NL80211_CMD_START_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006469 .policy = nl80211_policy,
6470 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006471 .doit = nl80211_start_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006472 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006473 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006474 },
6475 {
Johannes Berg88600202012-02-13 15:17:18 +01006476 .cmd = NL80211_CMD_STOP_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006477 .policy = nl80211_policy,
6478 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006479 .doit = nl80211_stop_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006480 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006481 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006482 },
Johannes Berg5727ef12007-12-19 02:03:34 +01006483 {
6484 .cmd = NL80211_CMD_GET_STATION,
6485 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006486 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01006487 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02006488 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6489 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006490 },
6491 {
6492 .cmd = NL80211_CMD_SET_STATION,
6493 .doit = nl80211_set_station,
6494 .policy = nl80211_policy,
6495 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006496 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006497 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006498 },
6499 {
6500 .cmd = NL80211_CMD_NEW_STATION,
6501 .doit = nl80211_new_station,
6502 .policy = nl80211_policy,
6503 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006504 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006505 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006506 },
6507 {
6508 .cmd = NL80211_CMD_DEL_STATION,
6509 .doit = nl80211_del_station,
6510 .policy = nl80211_policy,
6511 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006512 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006513 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006514 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006515 {
6516 .cmd = NL80211_CMD_GET_MPATH,
6517 .doit = nl80211_get_mpath,
6518 .dumpit = nl80211_dump_mpath,
6519 .policy = nl80211_policy,
6520 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006521 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006522 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006523 },
6524 {
6525 .cmd = NL80211_CMD_SET_MPATH,
6526 .doit = nl80211_set_mpath,
6527 .policy = nl80211_policy,
6528 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006529 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006530 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006531 },
6532 {
6533 .cmd = NL80211_CMD_NEW_MPATH,
6534 .doit = nl80211_new_mpath,
6535 .policy = nl80211_policy,
6536 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006537 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006538 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006539 },
6540 {
6541 .cmd = NL80211_CMD_DEL_MPATH,
6542 .doit = nl80211_del_mpath,
6543 .policy = nl80211_policy,
6544 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006545 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006546 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006547 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006548 {
6549 .cmd = NL80211_CMD_SET_BSS,
6550 .doit = nl80211_set_bss,
6551 .policy = nl80211_policy,
6552 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006553 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006554 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006555 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006556 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08006557 .cmd = NL80211_CMD_GET_REG,
6558 .doit = nl80211_get_reg,
6559 .policy = nl80211_policy,
6560 /* can be retrieved by unprivileged users */
6561 },
6562 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006563 .cmd = NL80211_CMD_SET_REG,
6564 .doit = nl80211_set_reg,
6565 .policy = nl80211_policy,
6566 .flags = GENL_ADMIN_PERM,
6567 },
6568 {
6569 .cmd = NL80211_CMD_REQ_SET_REG,
6570 .doit = nl80211_req_set_reg,
6571 .policy = nl80211_policy,
6572 .flags = GENL_ADMIN_PERM,
6573 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006574 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006575 .cmd = NL80211_CMD_GET_MESH_CONFIG,
6576 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006577 .policy = nl80211_policy,
6578 /* can be retrieved by unprivileged users */
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006579 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006580 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006581 },
6582 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006583 .cmd = NL80211_CMD_SET_MESH_CONFIG,
6584 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006585 .policy = nl80211_policy,
6586 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01006587 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006588 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006589 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02006590 {
Johannes Berg2a519312009-02-10 21:25:55 +01006591 .cmd = NL80211_CMD_TRIGGER_SCAN,
6592 .doit = nl80211_trigger_scan,
6593 .policy = nl80211_policy,
6594 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006595 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006596 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01006597 },
6598 {
6599 .cmd = NL80211_CMD_GET_SCAN,
6600 .policy = nl80211_policy,
6601 .dumpit = nl80211_dump_scan,
6602 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02006603 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03006604 .cmd = NL80211_CMD_START_SCHED_SCAN,
6605 .doit = nl80211_start_sched_scan,
6606 .policy = nl80211_policy,
6607 .flags = GENL_ADMIN_PERM,
6608 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6609 NL80211_FLAG_NEED_RTNL,
6610 },
6611 {
6612 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
6613 .doit = nl80211_stop_sched_scan,
6614 .policy = nl80211_policy,
6615 .flags = GENL_ADMIN_PERM,
6616 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6617 NL80211_FLAG_NEED_RTNL,
6618 },
6619 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02006620 .cmd = NL80211_CMD_AUTHENTICATE,
6621 .doit = nl80211_authenticate,
6622 .policy = nl80211_policy,
6623 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006624 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006625 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006626 },
6627 {
6628 .cmd = NL80211_CMD_ASSOCIATE,
6629 .doit = nl80211_associate,
6630 .policy = nl80211_policy,
6631 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006632 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006633 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006634 },
6635 {
6636 .cmd = NL80211_CMD_DEAUTHENTICATE,
6637 .doit = nl80211_deauthenticate,
6638 .policy = nl80211_policy,
6639 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006640 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006641 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006642 },
6643 {
6644 .cmd = NL80211_CMD_DISASSOCIATE,
6645 .doit = nl80211_disassociate,
6646 .policy = nl80211_policy,
6647 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006648 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006649 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006650 },
Johannes Berg04a773a2009-04-19 21:24:32 +02006651 {
6652 .cmd = NL80211_CMD_JOIN_IBSS,
6653 .doit = nl80211_join_ibss,
6654 .policy = nl80211_policy,
6655 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006656 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006657 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006658 },
6659 {
6660 .cmd = NL80211_CMD_LEAVE_IBSS,
6661 .doit = nl80211_leave_ibss,
6662 .policy = nl80211_policy,
6663 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006664 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006665 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006666 },
Johannes Bergaff89a92009-07-01 21:26:51 +02006667#ifdef CONFIG_NL80211_TESTMODE
6668 {
6669 .cmd = NL80211_CMD_TESTMODE,
6670 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006671 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02006672 .policy = nl80211_policy,
6673 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006674 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6675 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02006676 },
6677#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02006678 {
6679 .cmd = NL80211_CMD_CONNECT,
6680 .doit = nl80211_connect,
6681 .policy = nl80211_policy,
6682 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006683 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006684 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006685 },
6686 {
6687 .cmd = NL80211_CMD_DISCONNECT,
6688 .doit = nl80211_disconnect,
6689 .policy = nl80211_policy,
6690 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006691 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006692 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006693 },
Johannes Berg463d0182009-07-14 00:33:35 +02006694 {
6695 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
6696 .doit = nl80211_wiphy_netns,
6697 .policy = nl80211_policy,
6698 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006699 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6700 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02006701 },
Holger Schurig61fa7132009-11-11 12:25:40 +01006702 {
6703 .cmd = NL80211_CMD_GET_SURVEY,
6704 .policy = nl80211_policy,
6705 .dumpit = nl80211_dump_survey,
6706 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006707 {
6708 .cmd = NL80211_CMD_SET_PMKSA,
6709 .doit = nl80211_setdel_pmksa,
6710 .policy = nl80211_policy,
6711 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006712 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006713 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006714 },
6715 {
6716 .cmd = NL80211_CMD_DEL_PMKSA,
6717 .doit = nl80211_setdel_pmksa,
6718 .policy = nl80211_policy,
6719 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006720 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006721 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006722 },
6723 {
6724 .cmd = NL80211_CMD_FLUSH_PMKSA,
6725 .doit = nl80211_flush_pmksa,
6726 .policy = nl80211_policy,
6727 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006728 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006729 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006730 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006731 {
6732 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
6733 .doit = nl80211_remain_on_channel,
6734 .policy = nl80211_policy,
6735 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006736 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006737 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006738 },
6739 {
6740 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6741 .doit = nl80211_cancel_remain_on_channel,
6742 .policy = nl80211_policy,
6743 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006744 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006745 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006746 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006747 {
6748 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
6749 .doit = nl80211_set_tx_bitrate_mask,
6750 .policy = nl80211_policy,
6751 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006752 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6753 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006754 },
Jouni Malinen026331c2010-02-15 12:53:10 +02006755 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006756 .cmd = NL80211_CMD_REGISTER_FRAME,
6757 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006758 .policy = nl80211_policy,
6759 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006760 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6761 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006762 },
6763 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006764 .cmd = NL80211_CMD_FRAME,
6765 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006766 .policy = nl80211_policy,
6767 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006768 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006769 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006770 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02006771 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006772 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
6773 .doit = nl80211_tx_mgmt_cancel_wait,
6774 .policy = nl80211_policy,
6775 .flags = GENL_ADMIN_PERM,
6776 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6777 NL80211_FLAG_NEED_RTNL,
6778 },
6779 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02006780 .cmd = NL80211_CMD_SET_POWER_SAVE,
6781 .doit = nl80211_set_power_save,
6782 .policy = nl80211_policy,
6783 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006784 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6785 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006786 },
6787 {
6788 .cmd = NL80211_CMD_GET_POWER_SAVE,
6789 .doit = nl80211_get_power_save,
6790 .policy = nl80211_policy,
6791 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006792 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6793 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006794 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006795 {
6796 .cmd = NL80211_CMD_SET_CQM,
6797 .doit = nl80211_set_cqm,
6798 .policy = nl80211_policy,
6799 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006800 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6801 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006802 },
Johannes Bergf444de02010-05-05 15:25:02 +02006803 {
6804 .cmd = NL80211_CMD_SET_CHANNEL,
6805 .doit = nl80211_set_channel,
6806 .policy = nl80211_policy,
6807 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006808 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6809 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02006810 },
Bill Jordane8347eb2010-10-01 13:54:28 -04006811 {
6812 .cmd = NL80211_CMD_SET_WDS_PEER,
6813 .doit = nl80211_set_wds_peer,
6814 .policy = nl80211_policy,
6815 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02006816 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6817 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04006818 },
Johannes Berg29cbe682010-12-03 09:20:44 +01006819 {
6820 .cmd = NL80211_CMD_JOIN_MESH,
6821 .doit = nl80211_join_mesh,
6822 .policy = nl80211_policy,
6823 .flags = GENL_ADMIN_PERM,
6824 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6825 NL80211_FLAG_NEED_RTNL,
6826 },
6827 {
6828 .cmd = NL80211_CMD_LEAVE_MESH,
6829 .doit = nl80211_leave_mesh,
6830 .policy = nl80211_policy,
6831 .flags = GENL_ADMIN_PERM,
6832 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6833 NL80211_FLAG_NEED_RTNL,
6834 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02006835 {
6836 .cmd = NL80211_CMD_GET_WOWLAN,
6837 .doit = nl80211_get_wowlan,
6838 .policy = nl80211_policy,
6839 /* can be retrieved by unprivileged users */
6840 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6841 NL80211_FLAG_NEED_RTNL,
6842 },
6843 {
6844 .cmd = NL80211_CMD_SET_WOWLAN,
6845 .doit = nl80211_set_wowlan,
6846 .policy = nl80211_policy,
6847 .flags = GENL_ADMIN_PERM,
6848 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6849 NL80211_FLAG_NEED_RTNL,
6850 },
Johannes Berge5497d72011-07-05 16:35:40 +02006851 {
6852 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
6853 .doit = nl80211_set_rekey_data,
6854 .policy = nl80211_policy,
6855 .flags = GENL_ADMIN_PERM,
6856 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6857 NL80211_FLAG_NEED_RTNL,
6858 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03006859 {
6860 .cmd = NL80211_CMD_TDLS_MGMT,
6861 .doit = nl80211_tdls_mgmt,
6862 .policy = nl80211_policy,
6863 .flags = GENL_ADMIN_PERM,
6864 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6865 NL80211_FLAG_NEED_RTNL,
6866 },
6867 {
6868 .cmd = NL80211_CMD_TDLS_OPER,
6869 .doit = nl80211_tdls_oper,
6870 .policy = nl80211_policy,
6871 .flags = GENL_ADMIN_PERM,
6872 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6873 NL80211_FLAG_NEED_RTNL,
6874 },
Johannes Berg28946da2011-11-04 11:18:12 +01006875 {
6876 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
6877 .doit = nl80211_register_unexpected_frame,
6878 .policy = nl80211_policy,
6879 .flags = GENL_ADMIN_PERM,
6880 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6881 NL80211_FLAG_NEED_RTNL,
6882 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01006883 {
6884 .cmd = NL80211_CMD_PROBE_CLIENT,
6885 .doit = nl80211_probe_client,
6886 .policy = nl80211_policy,
6887 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006888 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg7f6cf312011-11-04 11:18:15 +01006889 NL80211_FLAG_NEED_RTNL,
6890 },
Johannes Berg5e760232011-11-04 11:18:17 +01006891 {
6892 .cmd = NL80211_CMD_REGISTER_BEACONS,
6893 .doit = nl80211_register_beacons,
6894 .policy = nl80211_policy,
6895 .flags = GENL_ADMIN_PERM,
6896 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6897 NL80211_FLAG_NEED_RTNL,
6898 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01006899 {
6900 .cmd = NL80211_CMD_SET_NOACK_MAP,
6901 .doit = nl80211_set_noack_map,
6902 .policy = nl80211_policy,
6903 .flags = GENL_ADMIN_PERM,
6904 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6905 NL80211_FLAG_NEED_RTNL,
6906 },
6907
Johannes Berg55682962007-09-20 13:09:35 -04006908};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006909
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006910static struct genl_multicast_group nl80211_mlme_mcgrp = {
6911 .name = "mlme",
6912};
Johannes Berg55682962007-09-20 13:09:35 -04006913
6914/* multicast groups */
6915static struct genl_multicast_group nl80211_config_mcgrp = {
6916 .name = "config",
6917};
Johannes Berg2a519312009-02-10 21:25:55 +01006918static struct genl_multicast_group nl80211_scan_mcgrp = {
6919 .name = "scan",
6920};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006921static struct genl_multicast_group nl80211_regulatory_mcgrp = {
6922 .name = "regulatory",
6923};
Johannes Berg55682962007-09-20 13:09:35 -04006924
6925/* notification functions */
6926
6927void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
6928{
6929 struct sk_buff *msg;
6930
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006931 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006932 if (!msg)
6933 return;
6934
6935 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
6936 nlmsg_free(msg);
6937 return;
6938 }
6939
Johannes Berg463d0182009-07-14 00:33:35 +02006940 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6941 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006942}
6943
Johannes Berg362a4152009-05-24 16:43:15 +02006944static int nl80211_add_scan_req(struct sk_buff *msg,
6945 struct cfg80211_registered_device *rdev)
6946{
6947 struct cfg80211_scan_request *req = rdev->scan_req;
6948 struct nlattr *nest;
6949 int i;
6950
Johannes Berg667503d2009-07-07 03:56:11 +02006951 ASSERT_RDEV_LOCK(rdev);
6952
Johannes Berg362a4152009-05-24 16:43:15 +02006953 if (WARN_ON(!req))
6954 return 0;
6955
6956 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
6957 if (!nest)
6958 goto nla_put_failure;
6959 for (i = 0; i < req->n_ssids; i++)
6960 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
6961 nla_nest_end(msg, nest);
6962
6963 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
6964 if (!nest)
6965 goto nla_put_failure;
6966 for (i = 0; i < req->n_channels; i++)
6967 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
6968 nla_nest_end(msg, nest);
6969
6970 if (req->ie)
6971 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
6972
6973 return 0;
6974 nla_put_failure:
6975 return -ENOBUFS;
6976}
6977
Johannes Berga538e2d2009-06-16 19:56:42 +02006978static int nl80211_send_scan_msg(struct sk_buff *msg,
6979 struct cfg80211_registered_device *rdev,
6980 struct net_device *netdev,
6981 u32 pid, u32 seq, int flags,
6982 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01006983{
6984 void *hdr;
6985
6986 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6987 if (!hdr)
6988 return -1;
6989
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05006990 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01006991 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6992
Johannes Berg362a4152009-05-24 16:43:15 +02006993 /* ignore errors and send incomplete event anyway */
6994 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01006995
6996 return genlmsg_end(msg, hdr);
6997
6998 nla_put_failure:
6999 genlmsg_cancel(msg, hdr);
7000 return -EMSGSIZE;
7001}
7002
Luciano Coelho807f8a82011-05-11 17:09:35 +03007003static int
7004nl80211_send_sched_scan_msg(struct sk_buff *msg,
7005 struct cfg80211_registered_device *rdev,
7006 struct net_device *netdev,
7007 u32 pid, u32 seq, int flags, u32 cmd)
7008{
7009 void *hdr;
7010
7011 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7012 if (!hdr)
7013 return -1;
7014
7015 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7016 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7017
7018 return genlmsg_end(msg, hdr);
7019
7020 nla_put_failure:
7021 genlmsg_cancel(msg, hdr);
7022 return -EMSGSIZE;
7023}
7024
Johannes Berga538e2d2009-06-16 19:56:42 +02007025void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
7026 struct net_device *netdev)
7027{
7028 struct sk_buff *msg;
7029
7030 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7031 if (!msg)
7032 return;
7033
7034 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7035 NL80211_CMD_TRIGGER_SCAN) < 0) {
7036 nlmsg_free(msg);
7037 return;
7038 }
7039
Johannes Berg463d0182009-07-14 00:33:35 +02007040 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7041 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02007042}
7043
Johannes Berg2a519312009-02-10 21:25:55 +01007044void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
7045 struct net_device *netdev)
7046{
7047 struct sk_buff *msg;
7048
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007049 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007050 if (!msg)
7051 return;
7052
Johannes Berga538e2d2009-06-16 19:56:42 +02007053 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7054 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007055 nlmsg_free(msg);
7056 return;
7057 }
7058
Johannes Berg463d0182009-07-14 00:33:35 +02007059 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7060 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007061}
7062
7063void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
7064 struct net_device *netdev)
7065{
7066 struct sk_buff *msg;
7067
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007068 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007069 if (!msg)
7070 return;
7071
Johannes Berga538e2d2009-06-16 19:56:42 +02007072 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7073 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007074 nlmsg_free(msg);
7075 return;
7076 }
7077
Johannes Berg463d0182009-07-14 00:33:35 +02007078 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7079 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007080}
7081
Luciano Coelho807f8a82011-05-11 17:09:35 +03007082void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
7083 struct net_device *netdev)
7084{
7085 struct sk_buff *msg;
7086
7087 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7088 if (!msg)
7089 return;
7090
7091 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
7092 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
7093 nlmsg_free(msg);
7094 return;
7095 }
7096
7097 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7098 nl80211_scan_mcgrp.id, GFP_KERNEL);
7099}
7100
7101void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
7102 struct net_device *netdev, u32 cmd)
7103{
7104 struct sk_buff *msg;
7105
7106 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7107 if (!msg)
7108 return;
7109
7110 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
7111 nlmsg_free(msg);
7112 return;
7113 }
7114
7115 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7116 nl80211_scan_mcgrp.id, GFP_KERNEL);
7117}
7118
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007119/*
7120 * This can happen on global regulatory changes or device specific settings
7121 * based on custom world regulatory domains.
7122 */
7123void nl80211_send_reg_change_event(struct regulatory_request *request)
7124{
7125 struct sk_buff *msg;
7126 void *hdr;
7127
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007128 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007129 if (!msg)
7130 return;
7131
7132 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
7133 if (!hdr) {
7134 nlmsg_free(msg);
7135 return;
7136 }
7137
7138 /* Userspace can always count this one always being set */
7139 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
7140
7141 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
7142 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7143 NL80211_REGDOM_TYPE_WORLD);
7144 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
7145 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7146 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
7147 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
7148 request->intersect)
7149 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7150 NL80211_REGDOM_TYPE_INTERSECTION);
7151 else {
7152 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7153 NL80211_REGDOM_TYPE_COUNTRY);
7154 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
7155 }
7156
7157 if (wiphy_idx_valid(request->wiphy_idx))
7158 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
7159
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007160 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007161
Johannes Bergbc43b282009-07-25 10:54:13 +02007162 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02007163 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02007164 GFP_ATOMIC);
7165 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007166
7167 return;
7168
7169nla_put_failure:
7170 genlmsg_cancel(msg, hdr);
7171 nlmsg_free(msg);
7172}
7173
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007174static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
7175 struct net_device *netdev,
7176 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007177 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007178{
7179 struct sk_buff *msg;
7180 void *hdr;
7181
Johannes Berge6d6e342009-07-01 21:26:47 +02007182 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007183 if (!msg)
7184 return;
7185
7186 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7187 if (!hdr) {
7188 nlmsg_free(msg);
7189 return;
7190 }
7191
7192 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7193 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7194 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7195
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007196 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007197
Johannes Berg463d0182009-07-14 00:33:35 +02007198 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7199 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007200 return;
7201
7202 nla_put_failure:
7203 genlmsg_cancel(msg, hdr);
7204 nlmsg_free(msg);
7205}
7206
7207void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007208 struct net_device *netdev, const u8 *buf,
7209 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007210{
7211 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007212 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007213}
7214
7215void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
7216 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007217 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007218{
Johannes Berge6d6e342009-07-01 21:26:47 +02007219 nl80211_send_mlme_event(rdev, netdev, buf, len,
7220 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007221}
7222
Jouni Malinen53b46b82009-03-27 20:53:56 +02007223void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007224 struct net_device *netdev, const u8 *buf,
7225 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007226{
7227 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007228 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007229}
7230
Jouni Malinen53b46b82009-03-27 20:53:56 +02007231void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
7232 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007233 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007234{
7235 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007236 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007237}
7238
Jouni Malinencf4e5942010-12-16 00:52:40 +02007239void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
7240 struct net_device *netdev, const u8 *buf,
7241 size_t len, gfp_t gfp)
7242{
7243 nl80211_send_mlme_event(rdev, netdev, buf, len,
7244 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
7245}
7246
7247void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
7248 struct net_device *netdev, const u8 *buf,
7249 size_t len, gfp_t gfp)
7250{
7251 nl80211_send_mlme_event(rdev, netdev, buf, len,
7252 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
7253}
7254
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04007255static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
7256 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02007257 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007258{
7259 struct sk_buff *msg;
7260 void *hdr;
7261
Johannes Berge6d6e342009-07-01 21:26:47 +02007262 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007263 if (!msg)
7264 return;
7265
7266 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7267 if (!hdr) {
7268 nlmsg_free(msg);
7269 return;
7270 }
7271
7272 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7273 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7274 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
7275 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7276
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007277 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03007278
Johannes Berg463d0182009-07-14 00:33:35 +02007279 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7280 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007281 return;
7282
7283 nla_put_failure:
7284 genlmsg_cancel(msg, hdr);
7285 nlmsg_free(msg);
7286}
7287
7288void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007289 struct net_device *netdev, const u8 *addr,
7290 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007291{
7292 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02007293 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007294}
7295
7296void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007297 struct net_device *netdev, const u8 *addr,
7298 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007299{
Johannes Berge6d6e342009-07-01 21:26:47 +02007300 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
7301 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007302}
7303
Samuel Ortizb23aa672009-07-01 21:26:54 +02007304void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
7305 struct net_device *netdev, const u8 *bssid,
7306 const u8 *req_ie, size_t req_ie_len,
7307 const u8 *resp_ie, size_t resp_ie_len,
7308 u16 status, gfp_t gfp)
7309{
7310 struct sk_buff *msg;
7311 void *hdr;
7312
7313 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7314 if (!msg)
7315 return;
7316
7317 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
7318 if (!hdr) {
7319 nlmsg_free(msg);
7320 return;
7321 }
7322
7323 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7324 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7325 if (bssid)
7326 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7327 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
7328 if (req_ie)
7329 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
7330 if (resp_ie)
7331 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
7332
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007333 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007334
Johannes Berg463d0182009-07-14 00:33:35 +02007335 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7336 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007337 return;
7338
7339 nla_put_failure:
7340 genlmsg_cancel(msg, hdr);
7341 nlmsg_free(msg);
7342
7343}
7344
7345void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
7346 struct net_device *netdev, const u8 *bssid,
7347 const u8 *req_ie, size_t req_ie_len,
7348 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
7349{
7350 struct sk_buff *msg;
7351 void *hdr;
7352
7353 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7354 if (!msg)
7355 return;
7356
7357 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
7358 if (!hdr) {
7359 nlmsg_free(msg);
7360 return;
7361 }
7362
7363 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7364 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7365 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7366 if (req_ie)
7367 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
7368 if (resp_ie)
7369 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
7370
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007371 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007372
Johannes Berg463d0182009-07-14 00:33:35 +02007373 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7374 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007375 return;
7376
7377 nla_put_failure:
7378 genlmsg_cancel(msg, hdr);
7379 nlmsg_free(msg);
7380
7381}
7382
7383void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
7384 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02007385 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02007386{
7387 struct sk_buff *msg;
7388 void *hdr;
7389
Johannes Berg667503d2009-07-07 03:56:11 +02007390 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007391 if (!msg)
7392 return;
7393
7394 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
7395 if (!hdr) {
7396 nlmsg_free(msg);
7397 return;
7398 }
7399
7400 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7401 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7402 if (from_ap && reason)
7403 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
7404 if (from_ap)
7405 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
7406 if (ie)
7407 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
7408
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007409 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007410
Johannes Berg463d0182009-07-14 00:33:35 +02007411 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7412 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007413 return;
7414
7415 nla_put_failure:
7416 genlmsg_cancel(msg, hdr);
7417 nlmsg_free(msg);
7418
7419}
7420
Johannes Berg04a773a2009-04-19 21:24:32 +02007421void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
7422 struct net_device *netdev, const u8 *bssid,
7423 gfp_t gfp)
7424{
7425 struct sk_buff *msg;
7426 void *hdr;
7427
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007428 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007429 if (!msg)
7430 return;
7431
7432 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
7433 if (!hdr) {
7434 nlmsg_free(msg);
7435 return;
7436 }
7437
7438 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7439 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7440 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7441
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007442 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02007443
Johannes Berg463d0182009-07-14 00:33:35 +02007444 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7445 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007446 return;
7447
7448 nla_put_failure:
7449 genlmsg_cancel(msg, hdr);
7450 nlmsg_free(msg);
7451}
7452
Javier Cardonac93b5e72011-04-07 15:08:34 -07007453void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
7454 struct net_device *netdev,
7455 const u8 *macaddr, const u8* ie, u8 ie_len,
7456 gfp_t gfp)
7457{
7458 struct sk_buff *msg;
7459 void *hdr;
7460
7461 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7462 if (!msg)
7463 return;
7464
7465 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
7466 if (!hdr) {
7467 nlmsg_free(msg);
7468 return;
7469 }
7470
7471 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7472 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7473 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
7474 if (ie_len && ie)
7475 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
7476
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007477 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07007478
7479 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7480 nl80211_mlme_mcgrp.id, gfp);
7481 return;
7482
7483 nla_put_failure:
7484 genlmsg_cancel(msg, hdr);
7485 nlmsg_free(msg);
7486}
7487
Jouni Malinena3b8b052009-03-27 21:59:49 +02007488void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
7489 struct net_device *netdev, const u8 *addr,
7490 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02007491 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02007492{
7493 struct sk_buff *msg;
7494 void *hdr;
7495
Johannes Berge6d6e342009-07-01 21:26:47 +02007496 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007497 if (!msg)
7498 return;
7499
7500 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
7501 if (!hdr) {
7502 nlmsg_free(msg);
7503 return;
7504 }
7505
7506 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7507 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7508 if (addr)
7509 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7510 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03007511 if (key_id != -1)
7512 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007513 if (tsc)
7514 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
7515
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007516 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007517
Johannes Berg463d0182009-07-14 00:33:35 +02007518 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7519 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007520 return;
7521
7522 nla_put_failure:
7523 genlmsg_cancel(msg, hdr);
7524 nlmsg_free(msg);
7525}
7526
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007527void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
7528 struct ieee80211_channel *channel_before,
7529 struct ieee80211_channel *channel_after)
7530{
7531 struct sk_buff *msg;
7532 void *hdr;
7533 struct nlattr *nl_freq;
7534
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007535 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007536 if (!msg)
7537 return;
7538
7539 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
7540 if (!hdr) {
7541 nlmsg_free(msg);
7542 return;
7543 }
7544
7545 /*
7546 * Since we are applying the beacon hint to a wiphy we know its
7547 * wiphy_idx is valid
7548 */
7549 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
7550
7551 /* Before */
7552 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
7553 if (!nl_freq)
7554 goto nla_put_failure;
7555 if (nl80211_msg_put_channel(msg, channel_before))
7556 goto nla_put_failure;
7557 nla_nest_end(msg, nl_freq);
7558
7559 /* After */
7560 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
7561 if (!nl_freq)
7562 goto nla_put_failure;
7563 if (nl80211_msg_put_channel(msg, channel_after))
7564 goto nla_put_failure;
7565 nla_nest_end(msg, nl_freq);
7566
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007567 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007568
Johannes Berg463d0182009-07-14 00:33:35 +02007569 rcu_read_lock();
7570 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
7571 GFP_ATOMIC);
7572 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007573
7574 return;
7575
7576nla_put_failure:
7577 genlmsg_cancel(msg, hdr);
7578 nlmsg_free(msg);
7579}
7580
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007581static void nl80211_send_remain_on_chan_event(
7582 int cmd, struct cfg80211_registered_device *rdev,
7583 struct net_device *netdev, u64 cookie,
7584 struct ieee80211_channel *chan,
7585 enum nl80211_channel_type channel_type,
7586 unsigned int duration, gfp_t gfp)
7587{
7588 struct sk_buff *msg;
7589 void *hdr;
7590
7591 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7592 if (!msg)
7593 return;
7594
7595 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7596 if (!hdr) {
7597 nlmsg_free(msg);
7598 return;
7599 }
7600
7601 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7602 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7603 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
7604 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
7605 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7606
7607 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
7608 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
7609
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007610 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007611
7612 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7613 nl80211_mlme_mcgrp.id, gfp);
7614 return;
7615
7616 nla_put_failure:
7617 genlmsg_cancel(msg, hdr);
7618 nlmsg_free(msg);
7619}
7620
7621void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
7622 struct net_device *netdev, u64 cookie,
7623 struct ieee80211_channel *chan,
7624 enum nl80211_channel_type channel_type,
7625 unsigned int duration, gfp_t gfp)
7626{
7627 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
7628 rdev, netdev, cookie, chan,
7629 channel_type, duration, gfp);
7630}
7631
7632void nl80211_send_remain_on_channel_cancel(
7633 struct cfg80211_registered_device *rdev, struct net_device *netdev,
7634 u64 cookie, struct ieee80211_channel *chan,
7635 enum nl80211_channel_type channel_type, gfp_t gfp)
7636{
7637 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7638 rdev, netdev, cookie, chan,
7639 channel_type, 0, gfp);
7640}
7641
Johannes Berg98b62182009-12-23 13:15:44 +01007642void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
7643 struct net_device *dev, const u8 *mac_addr,
7644 struct station_info *sinfo, gfp_t gfp)
7645{
7646 struct sk_buff *msg;
7647
7648 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7649 if (!msg)
7650 return;
7651
John W. Linville66266b32012-03-15 13:25:41 -04007652 if (nl80211_send_station(msg, 0, 0, 0,
7653 rdev, dev, mac_addr, sinfo) < 0) {
Johannes Berg98b62182009-12-23 13:15:44 +01007654 nlmsg_free(msg);
7655 return;
7656 }
7657
7658 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7659 nl80211_mlme_mcgrp.id, gfp);
7660}
7661
Jouni Malinenec15e682011-03-23 15:29:52 +02007662void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
7663 struct net_device *dev, const u8 *mac_addr,
7664 gfp_t gfp)
7665{
7666 struct sk_buff *msg;
7667 void *hdr;
7668
7669 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7670 if (!msg)
7671 return;
7672
7673 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
7674 if (!hdr) {
7675 nlmsg_free(msg);
7676 return;
7677 }
7678
7679 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7680 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
7681
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007682 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02007683
7684 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7685 nl80211_mlme_mcgrp.id, gfp);
7686 return;
7687
7688 nla_put_failure:
7689 genlmsg_cancel(msg, hdr);
7690 nlmsg_free(msg);
7691}
7692
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007693static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
7694 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01007695{
7696 struct wireless_dev *wdev = dev->ieee80211_ptr;
7697 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7698 struct sk_buff *msg;
7699 void *hdr;
7700 int err;
7701 u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
7702
7703 if (!nlpid)
7704 return false;
7705
7706 msg = nlmsg_new(100, gfp);
7707 if (!msg)
7708 return true;
7709
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007710 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01007711 if (!hdr) {
7712 nlmsg_free(msg);
7713 return true;
7714 }
7715
7716 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7717 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7718 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7719
7720 err = genlmsg_end(msg, hdr);
7721 if (err < 0) {
7722 nlmsg_free(msg);
7723 return true;
7724 }
7725
7726 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7727 return true;
7728
7729 nla_put_failure:
7730 genlmsg_cancel(msg, hdr);
7731 nlmsg_free(msg);
7732 return true;
7733}
7734
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007735bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
7736{
7737 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
7738 addr, gfp);
7739}
7740
7741bool nl80211_unexpected_4addr_frame(struct net_device *dev,
7742 const u8 *addr, gfp_t gfp)
7743{
7744 return __nl80211_unexpected_frame(dev,
7745 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
7746 addr, gfp);
7747}
7748
Johannes Berg2e161f72010-08-12 15:38:38 +02007749int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
7750 struct net_device *netdev, u32 nlpid,
Johannes Berg804483e2012-03-05 22:18:41 +01007751 int freq, int sig_dbm,
7752 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007753{
7754 struct sk_buff *msg;
7755 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02007756
7757 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7758 if (!msg)
7759 return -ENOMEM;
7760
Johannes Berg2e161f72010-08-12 15:38:38 +02007761 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02007762 if (!hdr) {
7763 nlmsg_free(msg);
7764 return -ENOMEM;
7765 }
7766
7767 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7768 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7769 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
Johannes Berg804483e2012-03-05 22:18:41 +01007770 if (sig_dbm)
7771 NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm);
Jouni Malinen026331c2010-02-15 12:53:10 +02007772 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7773
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007774 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007775
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007776 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
Jouni Malinen026331c2010-02-15 12:53:10 +02007777
7778 nla_put_failure:
7779 genlmsg_cancel(msg, hdr);
7780 nlmsg_free(msg);
7781 return -ENOBUFS;
7782}
7783
Johannes Berg2e161f72010-08-12 15:38:38 +02007784void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
7785 struct net_device *netdev, u64 cookie,
7786 const u8 *buf, size_t len, bool ack,
7787 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007788{
7789 struct sk_buff *msg;
7790 void *hdr;
7791
7792 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7793 if (!msg)
7794 return;
7795
Johannes Berg2e161f72010-08-12 15:38:38 +02007796 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02007797 if (!hdr) {
7798 nlmsg_free(msg);
7799 return;
7800 }
7801
7802 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7803 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7804 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7805 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7806 if (ack)
7807 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7808
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007809 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007810
7811 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
7812 return;
7813
7814 nla_put_failure:
7815 genlmsg_cancel(msg, hdr);
7816 nlmsg_free(msg);
7817}
7818
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007819void
7820nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
7821 struct net_device *netdev,
7822 enum nl80211_cqm_rssi_threshold_event rssi_event,
7823 gfp_t gfp)
7824{
7825 struct sk_buff *msg;
7826 struct nlattr *pinfoattr;
7827 void *hdr;
7828
7829 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7830 if (!msg)
7831 return;
7832
7833 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7834 if (!hdr) {
7835 nlmsg_free(msg);
7836 return;
7837 }
7838
7839 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7840 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7841
7842 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7843 if (!pinfoattr)
7844 goto nla_put_failure;
7845
7846 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
7847 rssi_event);
7848
7849 nla_nest_end(msg, pinfoattr);
7850
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007851 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007852
7853 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7854 nl80211_mlme_mcgrp.id, gfp);
7855 return;
7856
7857 nla_put_failure:
7858 genlmsg_cancel(msg, hdr);
7859 nlmsg_free(msg);
7860}
7861
Johannes Berge5497d72011-07-05 16:35:40 +02007862void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
7863 struct net_device *netdev, const u8 *bssid,
7864 const u8 *replay_ctr, gfp_t gfp)
7865{
7866 struct sk_buff *msg;
7867 struct nlattr *rekey_attr;
7868 void *hdr;
7869
7870 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7871 if (!msg)
7872 return;
7873
7874 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
7875 if (!hdr) {
7876 nlmsg_free(msg);
7877 return;
7878 }
7879
7880 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7881 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7882 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7883
7884 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
7885 if (!rekey_attr)
7886 goto nla_put_failure;
7887
7888 NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
7889 NL80211_REPLAY_CTR_LEN, replay_ctr);
7890
7891 nla_nest_end(msg, rekey_attr);
7892
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007893 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02007894
7895 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7896 nl80211_mlme_mcgrp.id, gfp);
7897 return;
7898
7899 nla_put_failure:
7900 genlmsg_cancel(msg, hdr);
7901 nlmsg_free(msg);
7902}
7903
Jouni Malinenc9df56b2011-09-16 18:56:23 +03007904void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
7905 struct net_device *netdev, int index,
7906 const u8 *bssid, bool preauth, gfp_t gfp)
7907{
7908 struct sk_buff *msg;
7909 struct nlattr *attr;
7910 void *hdr;
7911
7912 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7913 if (!msg)
7914 return;
7915
7916 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
7917 if (!hdr) {
7918 nlmsg_free(msg);
7919 return;
7920 }
7921
7922 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7923 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7924
7925 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
7926 if (!attr)
7927 goto nla_put_failure;
7928
7929 NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index);
7930 NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid);
7931 if (preauth)
7932 NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH);
7933
7934 nla_nest_end(msg, attr);
7935
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007936 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03007937
7938 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7939 nl80211_mlme_mcgrp.id, gfp);
7940 return;
7941
7942 nla_put_failure:
7943 genlmsg_cancel(msg, hdr);
7944 nlmsg_free(msg);
7945}
7946
Johannes Bergc063dbf2010-11-24 08:10:05 +01007947void
7948nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
7949 struct net_device *netdev, const u8 *peer,
7950 u32 num_packets, gfp_t gfp)
7951{
7952 struct sk_buff *msg;
7953 struct nlattr *pinfoattr;
7954 void *hdr;
7955
7956 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7957 if (!msg)
7958 return;
7959
7960 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7961 if (!hdr) {
7962 nlmsg_free(msg);
7963 return;
7964 }
7965
7966 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7967 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7968 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
7969
7970 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7971 if (!pinfoattr)
7972 goto nla_put_failure;
7973
7974 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
7975
7976 nla_nest_end(msg, pinfoattr);
7977
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007978 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01007979
7980 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7981 nl80211_mlme_mcgrp.id, gfp);
7982 return;
7983
7984 nla_put_failure:
7985 genlmsg_cancel(msg, hdr);
7986 nlmsg_free(msg);
7987}
7988
Johannes Berg7f6cf312011-11-04 11:18:15 +01007989void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
7990 u64 cookie, bool acked, gfp_t gfp)
7991{
7992 struct wireless_dev *wdev = dev->ieee80211_ptr;
7993 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7994 struct sk_buff *msg;
7995 void *hdr;
7996 int err;
7997
7998 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7999 if (!msg)
8000 return;
8001
8002 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
8003 if (!hdr) {
8004 nlmsg_free(msg);
8005 return;
8006 }
8007
8008 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8009 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
8010 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
8011 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
8012 if (acked)
8013 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
8014
8015 err = genlmsg_end(msg, hdr);
8016 if (err < 0) {
8017 nlmsg_free(msg);
8018 return;
8019 }
8020
8021 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8022 nl80211_mlme_mcgrp.id, gfp);
8023 return;
8024
8025 nla_put_failure:
8026 genlmsg_cancel(msg, hdr);
8027 nlmsg_free(msg);
8028}
8029EXPORT_SYMBOL(cfg80211_probe_status);
8030
Johannes Berg5e760232011-11-04 11:18:17 +01008031void cfg80211_report_obss_beacon(struct wiphy *wiphy,
8032 const u8 *frame, size_t len,
Johannes Berg804483e2012-03-05 22:18:41 +01008033 int freq, int sig_dbm, gfp_t gfp)
Johannes Berg5e760232011-11-04 11:18:17 +01008034{
8035 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
8036 struct sk_buff *msg;
8037 void *hdr;
8038 u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
8039
8040 if (!nlpid)
8041 return;
8042
8043 msg = nlmsg_new(len + 100, gfp);
8044 if (!msg)
8045 return;
8046
8047 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
8048 if (!hdr) {
8049 nlmsg_free(msg);
8050 return;
8051 }
8052
8053 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8054 if (freq)
8055 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
Johannes Berg804483e2012-03-05 22:18:41 +01008056 if (sig_dbm)
8057 NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm);
Johannes Berg5e760232011-11-04 11:18:17 +01008058 NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
8059
8060 genlmsg_end(msg, hdr);
8061
8062 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
8063 return;
8064
8065 nla_put_failure:
8066 genlmsg_cancel(msg, hdr);
8067 nlmsg_free(msg);
8068}
8069EXPORT_SYMBOL(cfg80211_report_obss_beacon);
8070
Jouni Malinen4918bd32013-01-15 13:36:11 -08008071void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
8072 enum nl80211_tdls_operation oper,
8073 u16 reason_code, gfp_t gfp)
8074{
8075 struct wireless_dev *wdev = dev->ieee80211_ptr;
8076 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
8077 struct sk_buff *msg;
8078 void *hdr;
8079 int err;
8080
8081 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
8082 if (!msg)
8083 return;
8084
8085 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
8086 if (!hdr) {
8087 nlmsg_free(msg);
8088 return;
8089 }
8090
8091 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8092 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
8093 nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
8094 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
8095 (reason_code > 0 &&
8096 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
8097 goto nla_put_failure;
8098
8099 err = genlmsg_end(msg, hdr);
8100 if (err < 0) {
8101 nlmsg_free(msg);
8102 return;
8103 }
8104
8105 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8106 nl80211_mlme_mcgrp.id, gfp);
8107 return;
8108
8109 nla_put_failure:
8110 genlmsg_cancel(msg, hdr);
8111 nlmsg_free(msg);
8112}
8113EXPORT_SYMBOL(cfg80211_tdls_oper_request);
8114
Jouni Malinen026331c2010-02-15 12:53:10 +02008115static int nl80211_netlink_notify(struct notifier_block * nb,
8116 unsigned long state,
8117 void *_notify)
8118{
8119 struct netlink_notify *notify = _notify;
8120 struct cfg80211_registered_device *rdev;
8121 struct wireless_dev *wdev;
8122
8123 if (state != NETLINK_URELEASE)
8124 return NOTIFY_DONE;
8125
8126 rcu_read_lock();
8127
Johannes Berg5e760232011-11-04 11:18:17 +01008128 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +02008129 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02008130 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Johannes Berg5e760232011-11-04 11:18:17 +01008131 if (rdev->ap_beacons_nlpid == notify->pid)
8132 rdev->ap_beacons_nlpid = 0;
8133 }
Jouni Malinen026331c2010-02-15 12:53:10 +02008134
8135 rcu_read_unlock();
8136
8137 return NOTIFY_DONE;
8138}
8139
8140static struct notifier_block nl80211_netlink_notifier = {
8141 .notifier_call = nl80211_netlink_notify,
8142};
8143
Johannes Berg55682962007-09-20 13:09:35 -04008144/* initialisation/exit functions */
8145
8146int nl80211_init(void)
8147{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008148 int err;
Johannes Berg55682962007-09-20 13:09:35 -04008149
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008150 err = genl_register_family_with_ops(&nl80211_fam,
8151 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04008152 if (err)
8153 return err;
8154
Johannes Berg55682962007-09-20 13:09:35 -04008155 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
8156 if (err)
8157 goto err_out;
8158
Johannes Berg2a519312009-02-10 21:25:55 +01008159 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
8160 if (err)
8161 goto err_out;
8162
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008163 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
8164 if (err)
8165 goto err_out;
8166
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008167 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
8168 if (err)
8169 goto err_out;
8170
Johannes Bergaff89a92009-07-01 21:26:51 +02008171#ifdef CONFIG_NL80211_TESTMODE
8172 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
8173 if (err)
8174 goto err_out;
8175#endif
8176
Jouni Malinen026331c2010-02-15 12:53:10 +02008177 err = netlink_register_notifier(&nl80211_netlink_notifier);
8178 if (err)
8179 goto err_out;
8180
Johannes Berg55682962007-09-20 13:09:35 -04008181 return 0;
8182 err_out:
8183 genl_unregister_family(&nl80211_fam);
8184 return err;
8185}
8186
8187void nl80211_exit(void)
8188{
Jouni Malinen026331c2010-02-15 12:53:10 +02008189 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04008190 genl_unregister_family(&nl80211_fam);
8191}