blob: 0fc46b36d5713b2a0f4c550e988be2e54d57ad9b [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
Deepthi Gowri6f79e162011-12-23 20:27:04 +053026static 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 Berg463d0182009-07-14 00:33:35 +020050static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020051 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040052 struct net_device **dev)
53{
Johannes Berg463d0182009-07-14 00:33:35 +020054 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040055 int ifindex;
56
Johannes Bergbba95fe2008-07-29 13:22:51 +020057 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040058 return -EINVAL;
59
Johannes Bergbba95fe2008-07-29 13:22:51 +020060 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020061 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040062 if (!*dev)
63 return -ENODEV;
64
Johannes Berg463d0182009-07-14 00:33:35 +020065 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020066 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040067 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020068 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040069 }
70
71 return 0;
72}
73
74/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000075static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040076 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
77 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070078 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020079 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020080 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053081 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020082 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
83 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
84 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
85 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010086 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040087
88 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
89 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
90 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010091
92 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020093 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010094
Johannes Bergb9454e82009-07-08 13:29:08 +020095 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010096 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
97 .len = WLAN_MAX_KEY_LEN },
98 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
99 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
100 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Deepthi Gowri5b26a952011-11-29 11:22:42 +0530101 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200102 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100103
104 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
105 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
106 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
107 .len = IEEE80211_MAX_DATA_LEN },
108 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
109 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100110 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
111 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
112 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
113 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
114 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100115 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100116 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200117 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100118 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
119 .len = IEEE80211_MAX_MESH_ID_LEN },
120 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300121
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700122 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
123 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
124
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300125 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
126 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
127 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200128 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
129 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100130 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300131
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800132 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700133 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700134
Jouni Malinen36aedc92008-08-25 11:58:58 +0300135 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
136 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200137
138 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
139 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
140 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100141 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
142 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200143
144 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
145 .len = IEEE80211_MAX_SSID_LEN },
146 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
147 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200148 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300149 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300150 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300151 [NL80211_ATTR_STA_FLAGS2] = {
152 .len = sizeof(struct nl80211_sta_flag_update),
153 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300154 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300155 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
156 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200157 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
158 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
159 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200160 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100161 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100162 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
163 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100164 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
165 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200166 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200167 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
168 .len = IEEE80211_MAX_DATA_LEN },
169 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200170 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200171 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300172 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200173 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300174 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
175 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200176 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900177 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
178 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100179 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100180 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100181 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200182 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700183 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300184 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Deepthi Gowri6f79e162011-12-23 20:27:04 +0530185 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
186 [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 },
Johannes Berg55682962007-09-20 13:09:35 -0400190};
191
Johannes Berge31b8212010-10-05 19:39:30 +0200192/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000193static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200194 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200195 [NL80211_KEY_IDX] = { .type = NLA_U8 },
196 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Deepthi Gowri5b26a952011-11-29 11:22:42 +0530197 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200198 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
199 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200200 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100201 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
202};
203
204/* policy for the key default flags */
205static const struct nla_policy
206nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
207 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
208 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200209};
210
Johannes Bergff1b6e62011-05-04 15:37:28 +0200211/* policy for WoWLAN attributes */
212static const struct nla_policy
213nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
214 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
215 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
216 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
217 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
218};
219
Holger Schuriga0438972009-11-11 11:30:02 +0100220/* ifidx get helper */
221static int nl80211_get_ifidx(struct netlink_callback *cb)
222{
223 int res;
224
225 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
226 nl80211_fam.attrbuf, nl80211_fam.maxattr,
227 nl80211_policy);
228 if (res)
229 return res;
230
231 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
232 return -EINVAL;
233
234 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
235 if (!res)
236 return -EINVAL;
237 return res;
238}
239
Johannes Berg67748892010-10-04 21:14:06 +0200240static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
241 struct netlink_callback *cb,
242 struct cfg80211_registered_device **rdev,
243 struct net_device **dev)
244{
245 int ifidx = cb->args[0];
246 int err;
247
248 if (!ifidx)
249 ifidx = nl80211_get_ifidx(cb);
250 if (ifidx < 0)
251 return ifidx;
252
253 cb->args[0] = ifidx;
254
255 rtnl_lock();
256
257 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
258 if (!*dev) {
259 err = -ENODEV;
260 goto out_rtnl;
261 }
262
263 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100264 if (IS_ERR(*rdev)) {
265 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200266 goto out_rtnl;
267 }
268
269 return 0;
270 out_rtnl:
271 rtnl_unlock();
272 return err;
273}
274
275static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
276{
277 cfg80211_unlock_rdev(rdev);
278 rtnl_unlock();
279}
280
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100281/* IE validation */
282static bool is_valid_ie_attr(const struct nlattr *attr)
283{
284 const u8 *pos;
285 int len;
286
287 if (!attr)
288 return true;
289
290 pos = nla_data(attr);
291 len = nla_len(attr);
292
293 while (len) {
294 u8 elemlen;
295
296 if (len < 2)
297 return false;
298 len -= 2;
299
300 elemlen = pos[1];
301 if (elemlen > len)
302 return false;
303
304 len -= elemlen;
305 pos += 2 + elemlen;
306 }
307
308 return true;
309}
310
Johannes Berg55682962007-09-20 13:09:35 -0400311/* message building helper */
312static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
313 int flags, u8 cmd)
314{
315 /* since there is no private header just add the generic one */
316 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
317}
318
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400319static int nl80211_msg_put_channel(struct sk_buff *msg,
320 struct ieee80211_channel *chan)
321{
322 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
323 chan->center_freq);
324
325 if (chan->flags & IEEE80211_CHAN_DISABLED)
326 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
327 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
328 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
329 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
330 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
331 if (chan->flags & IEEE80211_CHAN_RADAR)
332 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
333
334 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
335 DBM_TO_MBM(chan->max_power));
336
337 return 0;
338
339 nla_put_failure:
340 return -ENOBUFS;
341}
342
Johannes Berg55682962007-09-20 13:09:35 -0400343/* netlink command implementations */
344
Johannes Bergb9454e82009-07-08 13:29:08 +0200345struct key_parse {
346 struct key_params p;
347 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200348 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200349 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100350 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200351};
352
353static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
354{
355 struct nlattr *tb[NL80211_KEY_MAX + 1];
356 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
357 nl80211_key_policy);
358 if (err)
359 return err;
360
361 k->def = !!tb[NL80211_KEY_DEFAULT];
362 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
363
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100364 if (k->def) {
365 k->def_uni = true;
366 k->def_multi = true;
367 }
368 if (k->defmgmt)
369 k->def_multi = true;
370
Johannes Bergb9454e82009-07-08 13:29:08 +0200371 if (tb[NL80211_KEY_IDX])
372 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
373
374 if (tb[NL80211_KEY_DATA]) {
375 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
376 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
377 }
378
379 if (tb[NL80211_KEY_SEQ]) {
380 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
381 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
382 }
383
384 if (tb[NL80211_KEY_CIPHER])
385 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
386
Johannes Berge31b8212010-10-05 19:39:30 +0200387 if (tb[NL80211_KEY_TYPE]) {
388 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
389 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
390 return -EINVAL;
391 }
392
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100393 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
394 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
395 int err = nla_parse_nested(kdt,
396 NUM_NL80211_KEY_DEFAULT_TYPES - 1,
397 tb[NL80211_KEY_DEFAULT_TYPES],
398 nl80211_key_default_policy);
399 if (err)
400 return err;
401
402 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
403 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
404 }
405
Johannes Bergb9454e82009-07-08 13:29:08 +0200406 return 0;
407}
408
409static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
410{
411 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
412 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
413 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
414 }
415
416 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
417 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
418 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
419 }
420
421 if (info->attrs[NL80211_ATTR_KEY_IDX])
422 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
423
424 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
425 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
426
427 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
428 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
429
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100430 if (k->def) {
431 k->def_uni = true;
432 k->def_multi = true;
433 }
434 if (k->defmgmt)
435 k->def_multi = true;
436
Johannes Berge31b8212010-10-05 19:39:30 +0200437 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
438 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
439 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
440 return -EINVAL;
441 }
442
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100443 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
444 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
445 int err = nla_parse_nested(
446 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
447 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
448 nl80211_key_default_policy);
449 if (err)
450 return err;
451
452 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
453 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
454 }
455
Johannes Bergb9454e82009-07-08 13:29:08 +0200456 return 0;
457}
458
459static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
460{
461 int err;
462
463 memset(k, 0, sizeof(*k));
464 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200465 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200466
467 if (info->attrs[NL80211_ATTR_KEY])
468 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
469 else
470 err = nl80211_parse_key_old(info, k);
471
472 if (err)
473 return err;
474
475 if (k->def && k->defmgmt)
476 return -EINVAL;
477
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100478 if (k->defmgmt) {
479 if (k->def_uni || !k->def_multi)
480 return -EINVAL;
481 }
482
Johannes Bergb9454e82009-07-08 13:29:08 +0200483 if (k->idx != -1) {
484 if (k->defmgmt) {
485 if (k->idx < 4 || k->idx > 5)
486 return -EINVAL;
487 } else if (k->def) {
488 if (k->idx < 0 || k->idx > 3)
489 return -EINVAL;
490 } else {
491 if (k->idx < 0 || k->idx > 5)
492 return -EINVAL;
493 }
494 }
495
496 return 0;
497}
498
Johannes Bergfffd0932009-07-08 14:22:54 +0200499static struct cfg80211_cached_keys *
500nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
501 struct nlattr *keys)
502{
503 struct key_parse parse;
504 struct nlattr *key;
505 struct cfg80211_cached_keys *result;
506 int rem, err, def = 0;
507
508 result = kzalloc(sizeof(*result), GFP_KERNEL);
509 if (!result)
510 return ERR_PTR(-ENOMEM);
511
512 result->def = -1;
513 result->defmgmt = -1;
514
515 nla_for_each_nested(key, keys, rem) {
516 memset(&parse, 0, sizeof(parse));
517 parse.idx = -1;
518
519 err = nl80211_parse_key_new(key, &parse);
520 if (err)
521 goto error;
522 err = -EINVAL;
523 if (!parse.p.key)
524 goto error;
525 if (parse.idx < 0 || parse.idx > 4)
526 goto error;
527 if (parse.def) {
528 if (def)
529 goto error;
530 def = 1;
531 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100532 if (!parse.def_uni || !parse.def_multi)
533 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200534 } else if (parse.defmgmt)
535 goto error;
536 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200537 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200538 if (err)
539 goto error;
540 result->params[parse.idx].cipher = parse.p.cipher;
541 result->params[parse.idx].key_len = parse.p.key_len;
542 result->params[parse.idx].key = result->data[parse.idx];
543 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
544 }
545
546 return result;
547 error:
548 kfree(result);
549 return ERR_PTR(err);
550}
551
552static int nl80211_key_allowed(struct wireless_dev *wdev)
553{
554 ASSERT_WDEV_LOCK(wdev);
555
Johannes Bergfffd0932009-07-08 14:22:54 +0200556 switch (wdev->iftype) {
557 case NL80211_IFTYPE_AP:
558 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200559 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700560 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200561 break;
562 case NL80211_IFTYPE_ADHOC:
563 if (!wdev->current_bss)
564 return -ENOLINK;
565 break;
566 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200567 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200568 if (wdev->sme_state != CFG80211_SME_CONNECTED)
569 return -ENOLINK;
570 break;
571 default:
572 return -EINVAL;
573 }
574
575 return 0;
576}
577
Johannes Berg7527a782011-05-13 10:58:57 +0200578static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
579{
580 struct nlattr *nl_modes = nla_nest_start(msg, attr);
581 int i;
582
583 if (!nl_modes)
584 goto nla_put_failure;
585
586 i = 0;
587 while (ifmodes) {
588 if (ifmodes & 1)
589 NLA_PUT_FLAG(msg, i);
590 ifmodes >>= 1;
591 i++;
592 }
593
594 nla_nest_end(msg, nl_modes);
595 return 0;
596
597nla_put_failure:
598 return -ENOBUFS;
599}
600
601static int nl80211_put_iface_combinations(struct wiphy *wiphy,
602 struct sk_buff *msg)
603{
604 struct nlattr *nl_combis;
605 int i, j;
606
607 nl_combis = nla_nest_start(msg,
608 NL80211_ATTR_INTERFACE_COMBINATIONS);
609 if (!nl_combis)
610 goto nla_put_failure;
611
612 for (i = 0; i < wiphy->n_iface_combinations; i++) {
613 const struct ieee80211_iface_combination *c;
614 struct nlattr *nl_combi, *nl_limits;
615
616 c = &wiphy->iface_combinations[i];
617
618 nl_combi = nla_nest_start(msg, i + 1);
619 if (!nl_combi)
620 goto nla_put_failure;
621
622 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
623 if (!nl_limits)
624 goto nla_put_failure;
625
626 for (j = 0; j < c->n_limits; j++) {
627 struct nlattr *nl_limit;
628
629 nl_limit = nla_nest_start(msg, j + 1);
630 if (!nl_limit)
631 goto nla_put_failure;
632 NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
633 c->limits[j].max);
634 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
635 c->limits[j].types))
636 goto nla_put_failure;
637 nla_nest_end(msg, nl_limit);
638 }
639
640 nla_nest_end(msg, nl_limits);
641
642 if (c->beacon_int_infra_match)
643 NLA_PUT_FLAG(msg,
644 NL80211_IFACE_COMB_STA_AP_BI_MATCH);
645 NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
646 c->num_different_channels);
647 NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
648 c->max_interfaces);
649
650 nla_nest_end(msg, nl_combi);
651 }
652
653 nla_nest_end(msg, nl_combis);
654
655 return 0;
656nla_put_failure:
657 return -ENOBUFS;
658}
659
Johannes Berg55682962007-09-20 13:09:35 -0400660static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
661 struct cfg80211_registered_device *dev)
662{
663 void *hdr;
Johannes Bergee688b02008-01-24 19:38:39 +0100664 struct nlattr *nl_bands, *nl_band;
665 struct nlattr *nl_freqs, *nl_freq;
666 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100667 struct nlattr *nl_cmds;
Johannes Bergee688b02008-01-24 19:38:39 +0100668 enum ieee80211_band band;
669 struct ieee80211_channel *chan;
670 struct ieee80211_rate *rate;
671 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200672 const struct ieee80211_txrx_stypes *mgmt_stypes =
673 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400674
675 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
676 if (!hdr)
677 return -1;
678
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500679 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400680 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200681
Johannes Bergf5ea9122009-08-07 16:17:38 +0200682 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
683 cfg80211_rdev_list_generation);
684
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200685 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
686 dev->wiphy.retry_short);
687 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
688 dev->wiphy.retry_long);
689 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
690 dev->wiphy.frag_threshold);
691 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
692 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100693 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
694 dev->wiphy.coverage_class);
Johannes Berg2a519312009-02-10 21:25:55 +0100695 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
696 dev->wiphy.max_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200697 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
698 dev->wiphy.max_scan_ie_len);
Johannes Bergee688b02008-01-24 19:38:39 +0100699
Johannes Berge31b8212010-10-05 19:39:30 +0200700 if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
701 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
Javier Cardona15d5dda2011-04-07 15:08:28 -0700702 if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
703 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
Johannes Berge31b8212010-10-05 19:39:30 +0200704
Johannes Berg25e47c12009-04-02 20:14:06 +0200705 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
706 sizeof(u32) * dev->wiphy.n_cipher_suites,
707 dev->wiphy.cipher_suites);
708
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100709 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
710 dev->wiphy.max_num_pmkids);
711
Johannes Bergc0692b82010-08-27 14:26:53 +0300712 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
713 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
714
Bruno Randolf39fd5de2010-12-16 11:30:28 +0900715 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
716 dev->wiphy.available_antennas_tx);
717 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
718 dev->wiphy.available_antennas_rx);
719
Bruno Randolf7f531e02010-12-16 11:30:22 +0900720 if ((dev->wiphy.available_antennas_tx ||
721 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900722 u32 tx_ant = 0, rx_ant = 0;
723 int res;
724 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
725 if (!res) {
726 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
727 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
728 }
729 }
730
Johannes Berg7527a782011-05-13 10:58:57 +0200731 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
732 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700733 goto nla_put_failure;
734
Johannes Bergee688b02008-01-24 19:38:39 +0100735 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
736 if (!nl_bands)
737 goto nla_put_failure;
738
739 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
740 if (!dev->wiphy.bands[band])
741 continue;
742
743 nl_band = nla_nest_start(msg, band);
744 if (!nl_band)
745 goto nla_put_failure;
746
Johannes Bergd51626d2008-10-09 12:20:13 +0200747 /* add HT info */
748 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
749 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
750 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
751 &dev->wiphy.bands[band]->ht_cap.mcs);
752 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
753 dev->wiphy.bands[band]->ht_cap.cap);
754 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
755 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
756 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
757 dev->wiphy.bands[band]->ht_cap.ampdu_density);
758 }
759
Johannes Bergee688b02008-01-24 19:38:39 +0100760 /* add frequencies */
761 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
762 if (!nl_freqs)
763 goto nla_put_failure;
764
765 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
766 nl_freq = nla_nest_start(msg, i);
767 if (!nl_freq)
768 goto nla_put_failure;
769
770 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b02008-01-24 19:38:39 +0100771
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400772 if (nl80211_msg_put_channel(msg, chan))
773 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200774
Johannes Bergee688b02008-01-24 19:38:39 +0100775 nla_nest_end(msg, nl_freq);
776 }
777
778 nla_nest_end(msg, nl_freqs);
779
780 /* add bitrates */
781 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
782 if (!nl_rates)
783 goto nla_put_failure;
784
785 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
786 nl_rate = nla_nest_start(msg, i);
787 if (!nl_rate)
788 goto nla_put_failure;
789
790 rate = &dev->wiphy.bands[band]->bitrates[i];
791 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
792 rate->bitrate);
793 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
794 NLA_PUT_FLAG(msg,
795 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
796
797 nla_nest_end(msg, nl_rate);
798 }
799
800 nla_nest_end(msg, nl_rates);
801
802 nla_nest_end(msg, nl_band);
803 }
804 nla_nest_end(msg, nl_bands);
805
Johannes Berg8fdc6212009-03-14 09:34:01 +0100806 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
807 if (!nl_cmds)
808 goto nla_put_failure;
809
810 i = 0;
811#define CMD(op, n) \
812 do { \
813 if (dev->ops->op) { \
814 i++; \
815 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
816 } \
817 } while (0)
818
819 CMD(add_virtual_intf, NEW_INTERFACE);
820 CMD(change_virtual_intf, SET_INTERFACE);
821 CMD(add_key, NEW_KEY);
822 CMD(add_beacon, NEW_BEACON);
823 CMD(add_station, NEW_STATION);
824 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800825 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100826 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200827 CMD(auth, AUTHENTICATE);
828 CMD(assoc, ASSOCIATE);
829 CMD(deauth, DEAUTHENTICATE);
830 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200831 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100832 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100833 CMD(set_pmksa, SET_PMKSA);
834 CMD(del_pmksa, DEL_PMKSA);
835 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100836 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200837 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200838 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100839 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100840 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200841 i++;
842 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
843 }
Johannes Bergf444de02010-05-05 15:25:02 +0200844 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400845 CMD(set_wds_peer, SET_WDS_PEER);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300846 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
847 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100848
849#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200850
Johannes Berg6829c872009-07-02 09:13:27 +0200851 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200852 i++;
853 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
854 }
855
Johannes Berg6829c872009-07-02 09:13:27 +0200856 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200857 i++;
858 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
859 }
860
Johannes Berg8fdc6212009-03-14 09:34:01 +0100861 nla_nest_end(msg, nl_cmds);
862
Johannes Berga2939112010-12-14 17:54:28 +0100863 if (dev->ops->remain_on_channel)
864 NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
865 dev->wiphy.max_remain_on_channel_duration);
866
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100867 /* for now at least assume all drivers have it */
868 if (dev->ops->mgmt_tx)
869 NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
870
Johannes Berg2e161f72010-08-12 15:38:38 +0200871 if (mgmt_stypes) {
872 u16 stypes;
873 struct nlattr *nl_ftypes, *nl_ifs;
874 enum nl80211_iftype ift;
875
876 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
877 if (!nl_ifs)
878 goto nla_put_failure;
879
880 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
881 nl_ftypes = nla_nest_start(msg, ift);
882 if (!nl_ftypes)
883 goto nla_put_failure;
884 i = 0;
885 stypes = mgmt_stypes[ift].tx;
886 while (stypes) {
887 if (stypes & 1)
888 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
889 (i << 4) | IEEE80211_FTYPE_MGMT);
890 stypes >>= 1;
891 i++;
892 }
893 nla_nest_end(msg, nl_ftypes);
894 }
895
Johannes Berg74b70a42010-08-24 12:15:53 +0200896 nla_nest_end(msg, nl_ifs);
897
Johannes Berg2e161f72010-08-12 15:38:38 +0200898 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
899 if (!nl_ifs)
900 goto nla_put_failure;
901
902 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
903 nl_ftypes = nla_nest_start(msg, ift);
904 if (!nl_ftypes)
905 goto nla_put_failure;
906 i = 0;
907 stypes = mgmt_stypes[ift].rx;
908 while (stypes) {
909 if (stypes & 1)
910 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
911 (i << 4) | IEEE80211_FTYPE_MGMT);
912 stypes >>= 1;
913 i++;
914 }
915 nla_nest_end(msg, nl_ftypes);
916 }
917 nla_nest_end(msg, nl_ifs);
918 }
919
Johannes Bergff1b6e62011-05-04 15:37:28 +0200920 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
921 struct nlattr *nl_wowlan;
922
923 nl_wowlan = nla_nest_start(msg,
924 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
925 if (!nl_wowlan)
926 goto nla_put_failure;
927
928 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
929 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
930 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
931 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
932 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
933 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
934 if (dev->wiphy.wowlan.n_patterns) {
935 struct nl80211_wowlan_pattern_support pat = {
936 .max_patterns = dev->wiphy.wowlan.n_patterns,
937 .min_pattern_len =
938 dev->wiphy.wowlan.pattern_min_len,
939 .max_pattern_len =
940 dev->wiphy.wowlan.pattern_max_len,
941 };
942 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
943 sizeof(pat), &pat);
944 }
945
946 nla_nest_end(msg, nl_wowlan);
947 }
948
Johannes Berg7527a782011-05-13 10:58:57 +0200949 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
950 dev->wiphy.software_iftypes))
951 goto nla_put_failure;
952
953 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
954 goto nla_put_failure;
955
Johannes Berg55682962007-09-20 13:09:35 -0400956 return genlmsg_end(msg, hdr);
957
958 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700959 genlmsg_cancel(msg, hdr);
960 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400961}
962
963static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
964{
965 int idx = 0;
966 int start = cb->args[0];
967 struct cfg80211_registered_device *dev;
968
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500969 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200970 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200971 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
972 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200973 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400974 continue;
975 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
976 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200977 dev) < 0) {
978 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400979 break;
Julius Volzb4637272008-07-08 14:02:19 +0200980 }
Johannes Berg55682962007-09-20 13:09:35 -0400981 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500982 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -0400983
984 cb->args[0] = idx;
985
986 return skb->len;
987}
988
989static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
990{
991 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +0200992 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -0400993
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -0700994 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -0400995 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +0200996 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -0400997
Johannes Berg4c476992010-10-04 21:36:35 +0200998 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
999 nlmsg_free(msg);
1000 return -ENOBUFS;
1001 }
Johannes Berg55682962007-09-20 13:09:35 -04001002
Johannes Berg134e6372009-07-10 09:51:34 +00001003 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001004}
1005
Jouni Malinen31888482008-10-30 16:59:24 +02001006static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1007 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1008 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1009 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1010 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1011 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1012};
1013
1014static int parse_txq_params(struct nlattr *tb[],
1015 struct ieee80211_txq_params *txq_params)
1016{
1017 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
1018 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1019 !tb[NL80211_TXQ_ATTR_AIFS])
1020 return -EINVAL;
1021
1022 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
1023 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1024 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1025 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1026 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1027
1028 return 0;
1029}
1030
Johannes Bergf444de02010-05-05 15:25:02 +02001031static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1032{
1033 /*
1034 * You can only set the channel explicitly for AP, mesh
1035 * and WDS type interfaces; all others have their channel
1036 * managed via their respective "establish a connection"
1037 * command (connect, join, ...)
1038 *
1039 * Monitors are special as they are normally slaved to
1040 * whatever else is going on, so they behave as though
1041 * you tried setting the wiphy channel itself.
1042 */
1043 return !wdev ||
1044 wdev->iftype == NL80211_IFTYPE_AP ||
1045 wdev->iftype == NL80211_IFTYPE_WDS ||
1046 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001047 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1048 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001049}
1050
1051static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1052 struct wireless_dev *wdev,
1053 struct genl_info *info)
1054{
1055 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1056 u32 freq;
1057 int result;
1058
1059 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1060 return -EINVAL;
1061
1062 if (!nl80211_can_set_dev_channel(wdev))
1063 return -EOPNOTSUPP;
1064
1065 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1066 channel_type = nla_get_u32(info->attrs[
1067 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1068 if (channel_type != NL80211_CHAN_NO_HT &&
1069 channel_type != NL80211_CHAN_HT20 &&
1070 channel_type != NL80211_CHAN_HT40PLUS &&
1071 channel_type != NL80211_CHAN_HT40MINUS)
1072 return -EINVAL;
1073 }
1074
1075 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1076
1077 mutex_lock(&rdev->devlist_mtx);
1078 if (wdev) {
1079 wdev_lock(wdev);
1080 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
1081 wdev_unlock(wdev);
1082 } else {
1083 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
1084 }
1085 mutex_unlock(&rdev->devlist_mtx);
1086
1087 return result;
1088}
1089
1090static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1091{
Johannes Berg4c476992010-10-04 21:36:35 +02001092 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1093 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001094
Johannes Berg4c476992010-10-04 21:36:35 +02001095 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001096}
1097
Bill Jordane8347eb2010-10-01 13:54:28 -04001098static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1099{
Johannes Berg43b19952010-10-07 13:10:30 +02001100 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1101 struct net_device *dev = info->user_ptr[1];
1102 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001103 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001104
1105 if (!info->attrs[NL80211_ATTR_MAC])
1106 return -EINVAL;
1107
Johannes Berg43b19952010-10-07 13:10:30 +02001108 if (netif_running(dev))
1109 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001110
Johannes Berg43b19952010-10-07 13:10:30 +02001111 if (!rdev->ops->set_wds_peer)
1112 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001113
Johannes Berg43b19952010-10-07 13:10:30 +02001114 if (wdev->iftype != NL80211_IFTYPE_WDS)
1115 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001116
1117 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001118 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001119}
1120
1121
Johannes Berg55682962007-09-20 13:09:35 -04001122static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1123{
1124 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001125 struct net_device *netdev = NULL;
1126 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001127 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001128 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001129 u32 changed;
1130 u8 retry_short = 0, retry_long = 0;
1131 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001132 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001133
Johannes Bergf444de02010-05-05 15:25:02 +02001134 /*
1135 * Try to find the wiphy and netdev. Normally this
1136 * function shouldn't need the netdev, but this is
1137 * done for backward compatibility -- previously
1138 * setting the channel was done per wiphy, but now
1139 * it is per netdev. Previous userland like hostapd
1140 * also passed a netdev to set_wiphy, so that it is
1141 * possible to let that go to the right netdev!
1142 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001143 mutex_lock(&cfg80211_mutex);
1144
Johannes Bergf444de02010-05-05 15:25:02 +02001145 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1146 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1147
1148 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1149 if (netdev && netdev->ieee80211_ptr) {
1150 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1151 mutex_lock(&rdev->mtx);
1152 } else
1153 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001154 }
1155
Johannes Bergf444de02010-05-05 15:25:02 +02001156 if (!netdev) {
1157 rdev = __cfg80211_rdev_from_info(info);
1158 if (IS_ERR(rdev)) {
1159 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001160 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001161 }
1162 wdev = NULL;
1163 netdev = NULL;
1164 result = 0;
1165
1166 mutex_lock(&rdev->mtx);
1167 } else if (netif_running(netdev) &&
1168 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
1169 wdev = netdev->ieee80211_ptr;
1170 else
1171 wdev = NULL;
1172
1173 /*
1174 * end workaround code, by now the rdev is available
1175 * and locked, and wdev may or may not be NULL.
1176 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001177
1178 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001179 result = cfg80211_dev_rename(
1180 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001181
1182 mutex_unlock(&cfg80211_mutex);
1183
1184 if (result)
1185 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001186
Jouni Malinen31888482008-10-30 16:59:24 +02001187 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1188 struct ieee80211_txq_params txq_params;
1189 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1190
1191 if (!rdev->ops->set_txq_params) {
1192 result = -EOPNOTSUPP;
1193 goto bad_res;
1194 }
1195
1196 nla_for_each_nested(nl_txq_params,
1197 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1198 rem_txq_params) {
1199 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1200 nla_data(nl_txq_params),
1201 nla_len(nl_txq_params),
1202 txq_params_policy);
1203 result = parse_txq_params(tb, &txq_params);
1204 if (result)
1205 goto bad_res;
1206
1207 result = rdev->ops->set_txq_params(&rdev->wiphy,
1208 &txq_params);
1209 if (result)
1210 goto bad_res;
1211 }
1212 }
1213
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001214 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001215 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001216 if (result)
1217 goto bad_res;
1218 }
1219
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001220 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1221 enum nl80211_tx_power_setting type;
1222 int idx, mbm = 0;
1223
1224 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001225 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001226 goto bad_res;
1227 }
1228
1229 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1230 type = nla_get_u32(info->attrs[idx]);
1231
1232 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1233 (type != NL80211_TX_POWER_AUTOMATIC)) {
1234 result = -EINVAL;
1235 goto bad_res;
1236 }
1237
1238 if (type != NL80211_TX_POWER_AUTOMATIC) {
1239 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1240 mbm = nla_get_u32(info->attrs[idx]);
1241 }
1242
1243 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1244 if (result)
1245 goto bad_res;
1246 }
1247
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001248 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1249 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1250 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001251 if ((!rdev->wiphy.available_antennas_tx &&
1252 !rdev->wiphy.available_antennas_rx) ||
1253 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001254 result = -EOPNOTSUPP;
1255 goto bad_res;
1256 }
1257
1258 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1259 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1260
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001261 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001262 * available antenna masks, except for the "all" mask */
1263 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1264 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001265 result = -EINVAL;
1266 goto bad_res;
1267 }
1268
Bruno Randolf7f531e02010-12-16 11:30:22 +09001269 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1270 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001271
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001272 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1273 if (result)
1274 goto bad_res;
1275 }
1276
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001277 changed = 0;
1278
1279 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1280 retry_short = nla_get_u8(
1281 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1282 if (retry_short == 0) {
1283 result = -EINVAL;
1284 goto bad_res;
1285 }
1286 changed |= WIPHY_PARAM_RETRY_SHORT;
1287 }
1288
1289 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1290 retry_long = nla_get_u8(
1291 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1292 if (retry_long == 0) {
1293 result = -EINVAL;
1294 goto bad_res;
1295 }
1296 changed |= WIPHY_PARAM_RETRY_LONG;
1297 }
1298
1299 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1300 frag_threshold = nla_get_u32(
1301 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1302 if (frag_threshold < 256) {
1303 result = -EINVAL;
1304 goto bad_res;
1305 }
1306 if (frag_threshold != (u32) -1) {
1307 /*
1308 * Fragments (apart from the last one) are required to
1309 * have even length. Make the fragmentation code
1310 * simpler by stripping LSB should someone try to use
1311 * odd threshold value.
1312 */
1313 frag_threshold &= ~0x1;
1314 }
1315 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1316 }
1317
1318 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1319 rts_threshold = nla_get_u32(
1320 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1321 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1322 }
1323
Lukáš Turek81077e82009-12-21 22:50:47 +01001324 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1325 coverage_class = nla_get_u8(
1326 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1327 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1328 }
1329
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001330 if (changed) {
1331 u8 old_retry_short, old_retry_long;
1332 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001333 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001334
1335 if (!rdev->ops->set_wiphy_params) {
1336 result = -EOPNOTSUPP;
1337 goto bad_res;
1338 }
1339
1340 old_retry_short = rdev->wiphy.retry_short;
1341 old_retry_long = rdev->wiphy.retry_long;
1342 old_frag_threshold = rdev->wiphy.frag_threshold;
1343 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001344 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001345
1346 if (changed & WIPHY_PARAM_RETRY_SHORT)
1347 rdev->wiphy.retry_short = retry_short;
1348 if (changed & WIPHY_PARAM_RETRY_LONG)
1349 rdev->wiphy.retry_long = retry_long;
1350 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1351 rdev->wiphy.frag_threshold = frag_threshold;
1352 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1353 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001354 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1355 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001356
1357 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1358 if (result) {
1359 rdev->wiphy.retry_short = old_retry_short;
1360 rdev->wiphy.retry_long = old_retry_long;
1361 rdev->wiphy.frag_threshold = old_frag_threshold;
1362 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001363 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001364 }
1365 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001366
Johannes Berg306d6112008-12-08 12:39:04 +01001367 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001368 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001369 if (netdev)
1370 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001371 return result;
1372}
1373
1374
1375static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001376 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001377 struct net_device *dev)
1378{
1379 void *hdr;
1380
1381 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1382 if (!hdr)
1383 return -1;
1384
1385 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001386 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001387 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001388 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001389
1390 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1391 rdev->devlist_generation ^
1392 (cfg80211_rdev_list_generation << 2));
1393
Johannes Berg55682962007-09-20 13:09:35 -04001394 return genlmsg_end(msg, hdr);
1395
1396 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001397 genlmsg_cancel(msg, hdr);
1398 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001399}
1400
1401static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1402{
1403 int wp_idx = 0;
1404 int if_idx = 0;
1405 int wp_start = cb->args[0];
1406 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001407 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001408 struct wireless_dev *wdev;
1409
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001410 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001411 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1412 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001413 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001414 if (wp_idx < wp_start) {
1415 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001416 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001417 }
Johannes Berg55682962007-09-20 13:09:35 -04001418 if_idx = 0;
1419
Johannes Bergf5ea9122009-08-07 16:17:38 +02001420 mutex_lock(&rdev->devlist_mtx);
1421 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001422 if (if_idx < if_start) {
1423 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001424 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001425 }
Johannes Berg55682962007-09-20 13:09:35 -04001426 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1427 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001428 rdev, wdev->netdev) < 0) {
1429 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001430 goto out;
1431 }
1432 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001433 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001434 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001435
1436 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001437 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001438 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001439 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001440
1441 cb->args[0] = wp_idx;
1442 cb->args[1] = if_idx;
1443
1444 return skb->len;
1445}
1446
1447static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1448{
1449 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001450 struct cfg80211_registered_device *dev = info->user_ptr[0];
1451 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001452
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001453 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001454 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001455 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001456
Johannes Bergd7264052009-04-19 16:23:20 +02001457 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001458 dev, netdev) < 0) {
1459 nlmsg_free(msg);
1460 return -ENOBUFS;
1461 }
Johannes Berg55682962007-09-20 13:09:35 -04001462
Johannes Berg134e6372009-07-10 09:51:34 +00001463 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001464}
1465
Michael Wu66f7ac52008-01-31 19:48:22 +01001466static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1467 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1468 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1469 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1470 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1471 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1472};
1473
1474static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1475{
1476 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1477 int flag;
1478
1479 *mntrflags = 0;
1480
1481 if (!nla)
1482 return -EINVAL;
1483
1484 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1485 nla, mntr_flags_policy))
1486 return -EINVAL;
1487
1488 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1489 if (flags[flag])
1490 *mntrflags |= (1<<flag);
1491
1492 return 0;
1493}
1494
Johannes Berg9bc383d2009-11-19 11:55:19 +01001495static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001496 struct net_device *netdev, u8 use_4addr,
1497 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001498{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001499 if (!use_4addr) {
Jiri Pirkof350a0a2010-06-15 06:50:45 +00001500 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001501 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001502 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001503 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001504
1505 switch (iftype) {
1506 case NL80211_IFTYPE_AP_VLAN:
1507 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1508 return 0;
1509 break;
1510 case NL80211_IFTYPE_STATION:
1511 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1512 return 0;
1513 break;
1514 default:
1515 break;
1516 }
1517
1518 return -EOPNOTSUPP;
1519}
1520
Johannes Berg55682962007-09-20 13:09:35 -04001521static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1522{
Johannes Berg4c476992010-10-04 21:36:35 +02001523 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001524 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001525 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001526 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001527 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001528 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001529 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001530
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001531 memset(&params, 0, sizeof(params));
1532
Johannes Berg04a773a2009-04-19 21:24:32 +02001533 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001534
Johannes Berg723b0382008-09-16 20:22:09 +02001535 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001536 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001537 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001538 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001539 if (ntype > NL80211_IFTYPE_MAX)
1540 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001541 }
1542
Johannes Berg92ffe052008-09-16 20:39:36 +02001543 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001544 struct wireless_dev *wdev = dev->ieee80211_ptr;
1545
Johannes Berg4c476992010-10-04 21:36:35 +02001546 if (ntype != NL80211_IFTYPE_MESH_POINT)
1547 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001548 if (netif_running(dev))
1549 return -EBUSY;
1550
1551 wdev_lock(wdev);
1552 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1553 IEEE80211_MAX_MESH_ID_LEN);
1554 wdev->mesh_id_up_len =
1555 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1556 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1557 wdev->mesh_id_up_len);
1558 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001559 }
1560
Felix Fietkau8b787642009-11-10 18:53:10 +01001561 if (info->attrs[NL80211_ATTR_4ADDR]) {
1562 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1563 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001564 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001565 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001566 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001567 } else {
1568 params.use_4addr = -1;
1569 }
1570
Johannes Berg92ffe052008-09-16 20:39:36 +02001571 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001572 if (ntype != NL80211_IFTYPE_MONITOR)
1573 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001574 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1575 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001576 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001577 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001578
1579 flags = &_flags;
1580 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001581 }
Johannes Berg3b858752009-03-12 09:55:09 +01001582
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001583 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001584 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001585 else
1586 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001587
Johannes Berg9bc383d2009-11-19 11:55:19 +01001588 if (!err && params.use_4addr != -1)
1589 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1590
Johannes Berg55682962007-09-20 13:09:35 -04001591 return err;
1592}
1593
1594static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1595{
Johannes Berg4c476992010-10-04 21:36:35 +02001596 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001597 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001598 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001599 int err;
1600 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001601 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001602
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001603 memset(&params, 0, sizeof(params));
1604
Johannes Berg55682962007-09-20 13:09:35 -04001605 if (!info->attrs[NL80211_ATTR_IFNAME])
1606 return -EINVAL;
1607
1608 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1609 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1610 if (type > NL80211_IFTYPE_MAX)
1611 return -EINVAL;
1612 }
1613
Johannes Berg79c97e92009-07-07 03:56:12 +02001614 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001615 !(rdev->wiphy.interface_modes & (1 << type)))
1616 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001617
Johannes Berg9bc383d2009-11-19 11:55:19 +01001618 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001619 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001620 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001621 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001622 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001623 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001624
Michael Wu66f7ac52008-01-31 19:48:22 +01001625 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1626 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1627 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001628 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001629 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001630 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001631 if (IS_ERR(dev))
1632 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001633
Johannes Berg29cbe682010-12-03 09:20:44 +01001634 if (type == NL80211_IFTYPE_MESH_POINT &&
1635 info->attrs[NL80211_ATTR_MESH_ID]) {
1636 struct wireless_dev *wdev = dev->ieee80211_ptr;
1637
1638 wdev_lock(wdev);
1639 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1640 IEEE80211_MAX_MESH_ID_LEN);
1641 wdev->mesh_id_up_len =
1642 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1643 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1644 wdev->mesh_id_up_len);
1645 wdev_unlock(wdev);
1646 }
1647
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001648 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001649}
1650
1651static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1652{
Johannes Berg4c476992010-10-04 21:36:35 +02001653 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1654 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001655
Johannes Berg4c476992010-10-04 21:36:35 +02001656 if (!rdev->ops->del_virtual_intf)
1657 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001658
Johannes Berg4c476992010-10-04 21:36:35 +02001659 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001660}
1661
Johannes Berg41ade002007-12-19 02:03:29 +01001662struct get_key_cookie {
1663 struct sk_buff *msg;
1664 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001665 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001666};
1667
1668static void get_key_callback(void *c, struct key_params *params)
1669{
Johannes Bergb9454e82009-07-08 13:29:08 +02001670 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001671 struct get_key_cookie *cookie = c;
1672
1673 if (params->key)
1674 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1675 params->key_len, params->key);
1676
1677 if (params->seq)
1678 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1679 params->seq_len, params->seq);
1680
1681 if (params->cipher)
1682 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1683 params->cipher);
1684
Johannes Bergb9454e82009-07-08 13:29:08 +02001685 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1686 if (!key)
1687 goto nla_put_failure;
1688
1689 if (params->key)
1690 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1691 params->key_len, params->key);
1692
1693 if (params->seq)
1694 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1695 params->seq_len, params->seq);
1696
1697 if (params->cipher)
1698 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1699 params->cipher);
1700
1701 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1702
1703 nla_nest_end(cookie->msg, key);
1704
Johannes Berg41ade002007-12-19 02:03:29 +01001705 return;
1706 nla_put_failure:
1707 cookie->error = 1;
1708}
1709
1710static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1711{
Johannes Berg4c476992010-10-04 21:36:35 +02001712 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001713 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001714 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001715 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001716 const u8 *mac_addr = NULL;
1717 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001718 struct get_key_cookie cookie = {
1719 .error = 0,
1720 };
1721 void *hdr;
1722 struct sk_buff *msg;
1723
1724 if (info->attrs[NL80211_ATTR_KEY_IDX])
1725 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1726
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001727 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001728 return -EINVAL;
1729
1730 if (info->attrs[NL80211_ATTR_MAC])
1731 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1732
Johannes Berge31b8212010-10-05 19:39:30 +02001733 pairwise = !!mac_addr;
1734 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1735 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1736 if (kt >= NUM_NL80211_KEYTYPES)
1737 return -EINVAL;
1738 if (kt != NL80211_KEYTYPE_GROUP &&
1739 kt != NL80211_KEYTYPE_PAIRWISE)
1740 return -EINVAL;
1741 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1742 }
1743
Johannes Berg4c476992010-10-04 21:36:35 +02001744 if (!rdev->ops->get_key)
1745 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001746
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001747 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001748 if (!msg)
1749 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001750
1751 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1752 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001753 if (IS_ERR(hdr))
1754 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001755
1756 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001757 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001758
1759 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1760 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1761 if (mac_addr)
1762 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1763
Johannes Berge31b8212010-10-05 19:39:30 +02001764 if (pairwise && mac_addr &&
1765 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1766 return -ENOENT;
1767
1768 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1769 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001770
1771 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001772 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001773
1774 if (cookie.error)
1775 goto nla_put_failure;
1776
1777 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001778 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001779
1780 nla_put_failure:
1781 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001782 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001783 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001784 return err;
1785}
1786
1787static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1788{
Johannes Berg4c476992010-10-04 21:36:35 +02001789 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001790 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001791 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001792 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001793
Johannes Bergb9454e82009-07-08 13:29:08 +02001794 err = nl80211_parse_key(info, &key);
1795 if (err)
1796 return err;
1797
1798 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001799 return -EINVAL;
1800
Johannes Bergb9454e82009-07-08 13:29:08 +02001801 /* only support setting default key */
1802 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001803 return -EINVAL;
1804
Johannes Bergfffd0932009-07-08 14:22:54 +02001805 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001806
1807 if (key.def) {
1808 if (!rdev->ops->set_default_key) {
1809 err = -EOPNOTSUPP;
1810 goto out;
1811 }
1812
1813 err = nl80211_key_allowed(dev->ieee80211_ptr);
1814 if (err)
1815 goto out;
1816
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001817 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
1818 key.def_uni, key.def_multi);
1819
1820 if (err)
1821 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02001822
Johannes Berg3d23e342009-09-29 23:27:28 +02001823#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001824 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001825#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001826 } else {
1827 if (key.def_uni || !key.def_multi) {
1828 err = -EINVAL;
1829 goto out;
1830 }
1831
1832 if (!rdev->ops->set_default_mgmt_key) {
1833 err = -EOPNOTSUPP;
1834 goto out;
1835 }
1836
1837 err = nl80211_key_allowed(dev->ieee80211_ptr);
1838 if (err)
1839 goto out;
1840
1841 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
1842 dev, key.idx);
1843 if (err)
1844 goto out;
1845
1846#ifdef CONFIG_CFG80211_WEXT
1847 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
1848#endif
1849 }
1850
1851 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02001852 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001853
Johannes Berg41ade002007-12-19 02:03:29 +01001854 return err;
1855}
1856
1857static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1858{
Johannes Berg4c476992010-10-04 21:36:35 +02001859 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02001860 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001861 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02001862 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02001863 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01001864
Johannes Bergb9454e82009-07-08 13:29:08 +02001865 err = nl80211_parse_key(info, &key);
1866 if (err)
1867 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001868
Johannes Bergb9454e82009-07-08 13:29:08 +02001869 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001870 return -EINVAL;
1871
Johannes Berg41ade002007-12-19 02:03:29 +01001872 if (info->attrs[NL80211_ATTR_MAC])
1873 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1874
Johannes Berge31b8212010-10-05 19:39:30 +02001875 if (key.type == -1) {
1876 if (mac_addr)
1877 key.type = NL80211_KEYTYPE_PAIRWISE;
1878 else
1879 key.type = NL80211_KEYTYPE_GROUP;
1880 }
1881
1882 /* for now */
1883 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
1884 key.type != NL80211_KEYTYPE_GROUP)
1885 return -EINVAL;
1886
Johannes Berg4c476992010-10-04 21:36:35 +02001887 if (!rdev->ops->add_key)
1888 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001889
Johannes Berge31b8212010-10-05 19:39:30 +02001890 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
1891 key.type == NL80211_KEYTYPE_PAIRWISE,
1892 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02001893 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02001894
1895 wdev_lock(dev->ieee80211_ptr);
1896 err = nl80211_key_allowed(dev->ieee80211_ptr);
1897 if (!err)
1898 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02001899 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02001900 mac_addr, &key.p);
1901 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001902
Johannes Berg41ade002007-12-19 02:03:29 +01001903 return err;
1904}
1905
1906static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1907{
Johannes Berg4c476992010-10-04 21:36:35 +02001908 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001909 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001910 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001911 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001912 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001913
Johannes Bergb9454e82009-07-08 13:29:08 +02001914 err = nl80211_parse_key(info, &key);
1915 if (err)
1916 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001917
1918 if (info->attrs[NL80211_ATTR_MAC])
1919 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1920
Johannes Berge31b8212010-10-05 19:39:30 +02001921 if (key.type == -1) {
1922 if (mac_addr)
1923 key.type = NL80211_KEYTYPE_PAIRWISE;
1924 else
1925 key.type = NL80211_KEYTYPE_GROUP;
1926 }
1927
1928 /* for now */
1929 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
1930 key.type != NL80211_KEYTYPE_GROUP)
1931 return -EINVAL;
1932
Johannes Berg4c476992010-10-04 21:36:35 +02001933 if (!rdev->ops->del_key)
1934 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001935
Johannes Bergfffd0932009-07-08 14:22:54 +02001936 wdev_lock(dev->ieee80211_ptr);
1937 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02001938
1939 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
1940 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1941 err = -ENOENT;
1942
Johannes Bergfffd0932009-07-08 14:22:54 +02001943 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02001944 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
1945 key.type == NL80211_KEYTYPE_PAIRWISE,
1946 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001947
Johannes Berg3d23e342009-09-29 23:27:28 +02001948#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001949 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001950 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001951 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001952 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001953 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1954 }
1955#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001956 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001957
Johannes Berg41ade002007-12-19 02:03:29 +01001958 return err;
1959}
1960
Johannes Berged1b6cc2007-12-19 02:03:32 +01001961static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1962{
1963 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1964 struct beacon_parameters *info);
Johannes Berg4c476992010-10-04 21:36:35 +02001965 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1966 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02001967 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001968 struct beacon_parameters params;
Johannes Berg56d18932011-05-09 18:41:15 +02001969 int haveinfo = 0, err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001970
Deepthi Gowri6f79e162011-12-23 20:27:04 +05301971 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
1972 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
1973 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
1974 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001975 return -EINVAL;
1976
Johannes Berg074ac8d2010-09-16 14:58:22 +02001977 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02001978 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
1979 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02001980
Johannes Berg56d18932011-05-09 18:41:15 +02001981 memset(&params, 0, sizeof(params));
1982
Johannes Berged1b6cc2007-12-19 02:03:32 +01001983 switch (info->genlhdr->cmd) {
1984 case NL80211_CMD_NEW_BEACON:
1985 /* these are required for NEW_BEACON */
1986 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
1987 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
Johannes Berg4c476992010-10-04 21:36:35 +02001988 !info->attrs[NL80211_ATTR_BEACON_HEAD])
1989 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001990
Johannes Berg56d18932011-05-09 18:41:15 +02001991 params.interval =
1992 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
1993 params.dtim_period =
1994 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
1995
1996 err = cfg80211_validate_beacon_int(rdev, params.interval);
1997 if (err)
1998 return err;
1999
Deepthi Gowri6f79e162011-12-23 20:27:04 +05302000 /*
2001 * In theory, some of these attributes could be required for
2002 * NEW_BEACON, but since they were not used when the command was
2003 * originally added, keep them optional for old user space
2004 * programs to work with drivers that do not need the additional
2005 * information.
2006 */
2007 if (info->attrs[NL80211_ATTR_SSID]) {
2008 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2009 params.ssid_len =
2010 nla_len(info->attrs[NL80211_ATTR_SSID]);
2011 if (params.ssid_len == 0 ||
2012 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2013 return -EINVAL;
2014 }
2015
2016 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2017 params.hidden_ssid = nla_get_u32(
2018 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2019 if (params.hidden_ssid !=
2020 NL80211_HIDDEN_SSID_NOT_IN_USE &&
2021 params.hidden_ssid !=
2022 NL80211_HIDDEN_SSID_ZERO_LEN &&
2023 params.hidden_ssid !=
2024 NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2025 return -EINVAL;
2026 }
2027
2028 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2029
2030 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2031 params.auth_type = nla_get_u32(
2032 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2033 if (!nl80211_valid_auth_type(params.auth_type))
2034 return -EINVAL;
2035 } else
2036 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2037
2038 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2039 NL80211_MAX_NR_CIPHER_SUITES);
2040 if (err)
2041 return err;
2042
Johannes Berg79c97e92009-07-07 03:56:12 +02002043 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002044 break;
2045 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02002046 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002047 break;
2048 default:
2049 WARN_ON(1);
Johannes Berg4c476992010-10-04 21:36:35 +02002050 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002051 }
2052
Johannes Berg4c476992010-10-04 21:36:35 +02002053 if (!call)
2054 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002055
Johannes Berged1b6cc2007-12-19 02:03:32 +01002056 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
2057 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2058 params.head_len =
2059 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2060 haveinfo = 1;
2061 }
2062
2063 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
2064 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2065 params.tail_len =
2066 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2067 haveinfo = 1;
2068 }
2069
Johannes Berg4c476992010-10-04 21:36:35 +02002070 if (!haveinfo)
2071 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002072
Deepthi Gowri6f79e162011-12-23 20:27:04 +05302073 if (info->attrs[NL80211_ATTR_IE]) {
2074 params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2075 params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2076 }
2077
2078 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
2079 params.proberesp_ies =
2080 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2081 params.proberesp_ies_len =
2082 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2083 }
2084
2085 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
2086 params.assocresp_ies =
2087 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2088 params.assocresp_ies_len =
2089 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2090 }
2091
Johannes Berg56d18932011-05-09 18:41:15 +02002092 err = call(&rdev->wiphy, dev, &params);
2093 if (!err && params.interval)
2094 wdev->beacon_interval = params.interval;
2095 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002096}
2097
2098static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
2099{
Johannes Berg4c476992010-10-04 21:36:35 +02002100 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2101 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002102 struct wireless_dev *wdev = dev->ieee80211_ptr;
2103 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002104
Johannes Berg4c476992010-10-04 21:36:35 +02002105 if (!rdev->ops->del_beacon)
2106 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002107
Johannes Berg074ac8d2010-09-16 14:58:22 +02002108 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002109 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2110 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002111
Johannes Berg56d18932011-05-09 18:41:15 +02002112 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
2113 if (!err)
2114 wdev->beacon_interval = 0;
2115 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002116}
2117
Johannes Berg5727ef12007-12-19 02:03:34 +01002118static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2119 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2120 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2121 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002122 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002123 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002124};
2125
Johannes Bergeccb8e82009-05-11 21:57:56 +03002126static int parse_station_flags(struct genl_info *info,
2127 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002128{
2129 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002130 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002131 int flag;
2132
Johannes Bergeccb8e82009-05-11 21:57:56 +03002133 /*
2134 * Try parsing the new attribute first so userspace
2135 * can specify both for older kernels.
2136 */
2137 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2138 if (nla) {
2139 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002140
Johannes Bergeccb8e82009-05-11 21:57:56 +03002141 sta_flags = nla_data(nla);
2142 params->sta_flags_mask = sta_flags->mask;
2143 params->sta_flags_set = sta_flags->set;
2144 if ((params->sta_flags_mask |
2145 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2146 return -EINVAL;
2147 return 0;
2148 }
2149
2150 /* if present, parse the old attribute */
2151
2152 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002153 if (!nla)
2154 return 0;
2155
2156 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2157 nla, sta_flags_policy))
2158 return -EINVAL;
2159
Johannes Bergeccb8e82009-05-11 21:57:56 +03002160 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
2161 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002162
2163 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
2164 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03002165 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002166
2167 return 0;
2168}
2169
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002170static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2171 int attr)
2172{
2173 struct nlattr *rate;
2174 u16 bitrate;
2175
2176 rate = nla_nest_start(msg, attr);
2177 if (!rate)
2178 goto nla_put_failure;
2179
2180 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2181 bitrate = cfg80211_calculate_bitrate(info);
2182 if (bitrate > 0)
2183 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
2184
2185 if (info->flags & RATE_INFO_FLAGS_MCS)
2186 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs);
2187 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
2188 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
2189 if (info->flags & RATE_INFO_FLAGS_SHORT_GI)
2190 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
2191
2192 nla_nest_end(msg, rate);
2193 return true;
2194
2195nla_put_failure:
2196 return false;
2197}
2198
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002199static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
2200 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002201 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002202{
2203 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002204 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002205
2206 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2207 if (!hdr)
2208 return -1;
2209
2210 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2211 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
2212
Johannes Bergf5ea9122009-08-07 16:17:38 +02002213 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
2214
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002215 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2216 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002217 goto nla_put_failure;
Mohammed Shafi Shajakhanebe27c92011-04-08 21:24:24 +05302218 if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
2219 NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2220 sinfo->connected_time);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002221 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
2222 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2223 sinfo->inactive_time);
2224 if (sinfo->filled & STATION_INFO_RX_BYTES)
2225 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
2226 sinfo->rx_bytes);
2227 if (sinfo->filled & STATION_INFO_TX_BYTES)
2228 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
2229 sinfo->tx_bytes);
2230 if (sinfo->filled & STATION_INFO_LLID)
2231 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
2232 sinfo->llid);
2233 if (sinfo->filled & STATION_INFO_PLID)
2234 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
2235 sinfo->plid);
2236 if (sinfo->filled & STATION_INFO_PLINK_STATE)
2237 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
2238 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002239 if (sinfo->filled & STATION_INFO_SIGNAL)
2240 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
2241 sinfo->signal);
Bruno Randolf541a45a2010-12-02 19:12:43 +09002242 if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
2243 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2244 sinfo->signal_avg);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002245 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002246 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2247 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002248 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002249 }
2250 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2251 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2252 NL80211_STA_INFO_RX_BITRATE))
2253 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002254 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02002255 if (sinfo->filled & STATION_INFO_RX_PACKETS)
2256 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
2257 sinfo->rx_packets);
2258 if (sinfo->filled & STATION_INFO_TX_PACKETS)
2259 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
2260 sinfo->tx_packets);
Bruno Randolfb206b4e2010-10-06 18:34:12 +09002261 if (sinfo->filled & STATION_INFO_TX_RETRIES)
2262 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES,
2263 sinfo->tx_retries);
2264 if (sinfo->filled & STATION_INFO_TX_FAILED)
2265 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
2266 sinfo->tx_failed);
Paul Stewartf4263c92011-03-31 09:25:41 -07002267 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2268 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2269 if (!bss_param)
2270 goto nla_put_failure;
2271
2272 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
2273 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
2274 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
2275 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
2276 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
2277 NLA_PUT_FLAG(msg,
2278 NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
2279 NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2280 sinfo->bss_param.dtim_period);
2281 NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2282 sinfo->bss_param.beacon_interval);
2283
2284 nla_nest_end(msg, bss_param);
2285 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002286 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002287
Deepthi Gowri7ad229d2011-12-06 11:20:48 +05302288 if (sinfo->assoc_req_ies)
2289 NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2290 sinfo->assoc_req_ies);
2291
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002292 return genlmsg_end(msg, hdr);
2293
2294 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002295 genlmsg_cancel(msg, hdr);
2296 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002297}
2298
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002299static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002300 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002301{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002302 struct station_info sinfo;
2303 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002304 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002305 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002306 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002307 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002308
Johannes Berg67748892010-10-04 21:14:06 +02002309 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2310 if (err)
2311 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002312
2313 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002314 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002315 goto out_err;
2316 }
2317
Johannes Bergbba95fe2008-07-29 13:22:51 +02002318 while (1) {
Deepthi Gowri7ad229d2011-12-06 11:20:48 +05302319 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002320 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2321 mac_addr, &sinfo);
2322 if (err == -ENOENT)
2323 break;
2324 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002325 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002326
2327 if (nl80211_send_station(skb,
2328 NETLINK_CB(cb->skb).pid,
2329 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2330 netdev, mac_addr,
2331 &sinfo) < 0)
2332 goto out;
2333
2334 sta_idx++;
2335 }
2336
2337
2338 out:
2339 cb->args[1] = sta_idx;
2340 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002341 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002342 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002343
2344 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002345}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002346
Johannes Berg5727ef12007-12-19 02:03:34 +01002347static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2348{
Johannes Berg4c476992010-10-04 21:36:35 +02002349 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2350 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002351 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002352 struct sk_buff *msg;
2353 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002354 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002355
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002356 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002357
2358 if (!info->attrs[NL80211_ATTR_MAC])
2359 return -EINVAL;
2360
2361 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2362
Johannes Berg4c476992010-10-04 21:36:35 +02002363 if (!rdev->ops->get_station)
2364 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002365
Johannes Berg79c97e92009-07-07 03:56:12 +02002366 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002367 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002368 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002369
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002370 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002371 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002372 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002373
2374 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002375 dev, mac_addr, &sinfo) < 0) {
2376 nlmsg_free(msg);
2377 return -ENOBUFS;
2378 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002379
Johannes Berg4c476992010-10-04 21:36:35 +02002380 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002381}
2382
2383/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002384 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002385 */
Johannes Berg463d0182009-07-14 00:33:35 +02002386static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01002387 struct cfg80211_registered_device *rdev,
2388 struct net_device **vlan)
2389{
Johannes Berg463d0182009-07-14 00:33:35 +02002390 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01002391 *vlan = NULL;
2392
2393 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02002394 *vlan = dev_get_by_index(genl_info_net(info),
2395 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01002396 if (!*vlan)
2397 return -ENODEV;
2398 if (!(*vlan)->ieee80211_ptr)
2399 return -EINVAL;
2400 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
2401 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002402 if (!netif_running(*vlan))
2403 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01002404 }
2405 return 0;
2406}
2407
2408static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2409{
Johannes Berg4c476992010-10-04 21:36:35 +02002410 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002411 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002412 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002413 struct station_parameters params;
2414 u8 *mac_addr = NULL;
2415
2416 memset(&params, 0, sizeof(params));
2417
2418 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002419 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002420
2421 if (info->attrs[NL80211_ATTR_STA_AID])
2422 return -EINVAL;
2423
2424 if (!info->attrs[NL80211_ATTR_MAC])
2425 return -EINVAL;
2426
2427 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2428
2429 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2430 params.supported_rates =
2431 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2432 params.supported_rates_len =
2433 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2434 }
2435
2436 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2437 params.listen_interval =
2438 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2439
Jouni Malinen36aedc92008-08-25 11:58:58 +03002440 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2441 params.ht_capa =
2442 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2443
Johannes Bergeccb8e82009-05-11 21:57:56 +03002444 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002445 return -EINVAL;
2446
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002447 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2448 params.plink_action =
2449 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2450
Javier Cardona9c3990a2011-05-03 16:57:11 -07002451 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2452 params.plink_state =
2453 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2454
Johannes Berg463d0182009-07-14 00:33:35 +02002455 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002456 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02002457 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002458
2459 /* validate settings */
2460 err = 0;
2461
2462 switch (dev->ieee80211_ptr->iftype) {
2463 case NL80211_IFTYPE_AP:
2464 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002465 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002466 /* disallow mesh-specific things */
2467 if (params.plink_action)
2468 err = -EINVAL;
2469 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002470 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002471 case NL80211_IFTYPE_STATION:
2472 /* disallow everything but AUTHORIZED flag */
2473 if (params.plink_action)
2474 err = -EINVAL;
2475 if (params.vlan)
2476 err = -EINVAL;
2477 if (params.supported_rates)
2478 err = -EINVAL;
2479 if (params.ht_capa)
2480 err = -EINVAL;
2481 if (params.listen_interval >= 0)
2482 err = -EINVAL;
2483 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2484 err = -EINVAL;
2485 break;
2486 case NL80211_IFTYPE_MESH_POINT:
2487 /* disallow things mesh doesn't support */
2488 if (params.vlan)
2489 err = -EINVAL;
2490 if (params.ht_capa)
2491 err = -EINVAL;
2492 if (params.listen_interval >= 0)
2493 err = -EINVAL;
Javier Cardonab39c48f2011-04-07 15:08:30 -07002494 if (params.sta_flags_mask &
2495 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002496 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002497 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Berga97f4422009-06-18 17:23:43 +02002498 err = -EINVAL;
2499 break;
2500 default:
2501 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02002502 }
2503
Johannes Berg5727ef12007-12-19 02:03:34 +01002504 if (err)
2505 goto out;
2506
Johannes Berg79c97e92009-07-07 03:56:12 +02002507 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002508 err = -EOPNOTSUPP;
2509 goto out;
2510 }
2511
Johannes Berg79c97e92009-07-07 03:56:12 +02002512 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002513
2514 out:
2515 if (params.vlan)
2516 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002517
Johannes Berg5727ef12007-12-19 02:03:34 +01002518 return err;
2519}
2520
2521static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2522{
Johannes Berg4c476992010-10-04 21:36:35 +02002523 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002524 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002525 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002526 struct station_parameters params;
2527 u8 *mac_addr = NULL;
2528
2529 memset(&params, 0, sizeof(params));
2530
2531 if (!info->attrs[NL80211_ATTR_MAC])
2532 return -EINVAL;
2533
Johannes Berg5727ef12007-12-19 02:03:34 +01002534 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2535 return -EINVAL;
2536
2537 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2538 return -EINVAL;
2539
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002540 if (!info->attrs[NL80211_ATTR_STA_AID])
2541 return -EINVAL;
2542
Johannes Berg5727ef12007-12-19 02:03:34 +01002543 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2544 params.supported_rates =
2545 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2546 params.supported_rates_len =
2547 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2548 params.listen_interval =
2549 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002550
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002551 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2552 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2553 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002554
Jouni Malinen36aedc92008-08-25 11:58:58 +03002555 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2556 params.ht_capa =
2557 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002558
Javier Cardona96b78df2011-04-07 15:08:33 -07002559 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2560 params.plink_action =
2561 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2562
Johannes Bergeccb8e82009-05-11 21:57:56 +03002563 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002564 return -EINVAL;
2565
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002566 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002567 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardona96b78df2011-04-07 15:08:33 -07002568 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002569 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2570 return -EINVAL;
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002571
Johannes Berg463d0182009-07-14 00:33:35 +02002572 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002573 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002574 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002575
2576 /* validate settings */
2577 err = 0;
2578
Johannes Berg79c97e92009-07-07 03:56:12 +02002579 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002580 err = -EOPNOTSUPP;
2581 goto out;
2582 }
2583
Johannes Berg79c97e92009-07-07 03:56:12 +02002584 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002585
2586 out:
2587 if (params.vlan)
2588 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01002589 return err;
2590}
2591
2592static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2593{
Johannes Berg4c476992010-10-04 21:36:35 +02002594 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2595 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002596 u8 *mac_addr = NULL;
2597
2598 if (info->attrs[NL80211_ATTR_MAC])
2599 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2600
Johannes Berge80cf852009-05-11 14:43:13 +02002601 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002602 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002603 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002604 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2605 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002606
Johannes Berg4c476992010-10-04 21:36:35 +02002607 if (!rdev->ops->del_station)
2608 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01002609
Johannes Berg4c476992010-10-04 21:36:35 +02002610 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002611}
2612
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002613static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2614 int flags, struct net_device *dev,
2615 u8 *dst, u8 *next_hop,
2616 struct mpath_info *pinfo)
2617{
2618 void *hdr;
2619 struct nlattr *pinfoattr;
2620
2621 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2622 if (!hdr)
2623 return -1;
2624
2625 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2626 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2627 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2628
Johannes Bergf5ea9122009-08-07 16:17:38 +02002629 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2630
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002631 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2632 if (!pinfoattr)
2633 goto nla_put_failure;
2634 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2635 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2636 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002637 if (pinfo->filled & MPATH_INFO_SN)
2638 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2639 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002640 if (pinfo->filled & MPATH_INFO_METRIC)
2641 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2642 pinfo->metric);
2643 if (pinfo->filled & MPATH_INFO_EXPTIME)
2644 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2645 pinfo->exptime);
2646 if (pinfo->filled & MPATH_INFO_FLAGS)
2647 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2648 pinfo->flags);
2649 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2650 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2651 pinfo->discovery_timeout);
2652 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2653 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2654 pinfo->discovery_retries);
2655
2656 nla_nest_end(msg, pinfoattr);
2657
2658 return genlmsg_end(msg, hdr);
2659
2660 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002661 genlmsg_cancel(msg, hdr);
2662 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002663}
2664
2665static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002666 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002667{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002668 struct mpath_info pinfo;
2669 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002670 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002671 u8 dst[ETH_ALEN];
2672 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002673 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002674 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002675
Johannes Berg67748892010-10-04 21:14:06 +02002676 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2677 if (err)
2678 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002679
2680 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002681 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002682 goto out_err;
2683 }
2684
Jouni Malineneec60b02009-03-20 21:21:19 +02002685 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2686 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002687 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002688 }
2689
Johannes Bergbba95fe2008-07-29 13:22:51 +02002690 while (1) {
2691 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2692 dst, next_hop, &pinfo);
2693 if (err == -ENOENT)
2694 break;
2695 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002696 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002697
2698 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2699 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2700 netdev, dst, next_hop,
2701 &pinfo) < 0)
2702 goto out;
2703
2704 path_idx++;
2705 }
2706
2707
2708 out:
2709 cb->args[1] = path_idx;
2710 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002711 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002712 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002713 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002714}
2715
2716static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2717{
Johannes Berg4c476992010-10-04 21:36:35 +02002718 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002719 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002720 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002721 struct mpath_info pinfo;
2722 struct sk_buff *msg;
2723 u8 *dst = NULL;
2724 u8 next_hop[ETH_ALEN];
2725
2726 memset(&pinfo, 0, sizeof(pinfo));
2727
2728 if (!info->attrs[NL80211_ATTR_MAC])
2729 return -EINVAL;
2730
2731 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2732
Johannes Berg4c476992010-10-04 21:36:35 +02002733 if (!rdev->ops->get_mpath)
2734 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002735
Johannes Berg4c476992010-10-04 21:36:35 +02002736 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2737 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002738
Johannes Berg79c97e92009-07-07 03:56:12 +02002739 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002740 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002741 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002742
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002743 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002744 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002745 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002746
2747 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002748 dev, dst, next_hop, &pinfo) < 0) {
2749 nlmsg_free(msg);
2750 return -ENOBUFS;
2751 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002752
Johannes Berg4c476992010-10-04 21:36:35 +02002753 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002754}
2755
2756static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2757{
Johannes Berg4c476992010-10-04 21:36:35 +02002758 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2759 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002760 u8 *dst = NULL;
2761 u8 *next_hop = NULL;
2762
2763 if (!info->attrs[NL80211_ATTR_MAC])
2764 return -EINVAL;
2765
2766 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2767 return -EINVAL;
2768
2769 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2770 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2771
Johannes Berg4c476992010-10-04 21:36:35 +02002772 if (!rdev->ops->change_mpath)
2773 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002774
Johannes Berg4c476992010-10-04 21:36:35 +02002775 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2776 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002777
Johannes Berg4c476992010-10-04 21:36:35 +02002778 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002779}
Johannes Berg4c476992010-10-04 21:36:35 +02002780
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002781static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2782{
Johannes Berg4c476992010-10-04 21:36:35 +02002783 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2784 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002785 u8 *dst = NULL;
2786 u8 *next_hop = NULL;
2787
2788 if (!info->attrs[NL80211_ATTR_MAC])
2789 return -EINVAL;
2790
2791 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2792 return -EINVAL;
2793
2794 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2795 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2796
Johannes Berg4c476992010-10-04 21:36:35 +02002797 if (!rdev->ops->add_mpath)
2798 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002799
Johannes Berg4c476992010-10-04 21:36:35 +02002800 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2801 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002802
Johannes Berg4c476992010-10-04 21:36:35 +02002803 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002804}
2805
2806static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2807{
Johannes Berg4c476992010-10-04 21:36:35 +02002808 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2809 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002810 u8 *dst = NULL;
2811
2812 if (info->attrs[NL80211_ATTR_MAC])
2813 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2814
Johannes Berg4c476992010-10-04 21:36:35 +02002815 if (!rdev->ops->del_mpath)
2816 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002817
Johannes Berg4c476992010-10-04 21:36:35 +02002818 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002819}
2820
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002821static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2822{
Johannes Berg4c476992010-10-04 21:36:35 +02002823 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2824 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002825 struct bss_parameters params;
2826
2827 memset(&params, 0, sizeof(params));
2828 /* default to not changing parameters */
2829 params.use_cts_prot = -1;
2830 params.use_short_preamble = -1;
2831 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002832 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01002833 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002834
2835 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2836 params.use_cts_prot =
2837 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2838 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2839 params.use_short_preamble =
2840 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2841 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2842 params.use_short_slot_time =
2843 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002844 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2845 params.basic_rates =
2846 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2847 params.basic_rates_len =
2848 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2849 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002850 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
2851 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01002852 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
2853 params.ht_opmode =
2854 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002855
Johannes Berg4c476992010-10-04 21:36:35 +02002856 if (!rdev->ops->change_bss)
2857 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002858
Johannes Berg074ac8d2010-09-16 14:58:22 +02002859 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002860 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2861 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002862
Johannes Berg4c476992010-10-04 21:36:35 +02002863 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002864}
2865
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002866static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002867 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2868 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2869 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2870 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2871 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2872 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2873};
2874
2875static int parse_reg_rule(struct nlattr *tb[],
2876 struct ieee80211_reg_rule *reg_rule)
2877{
2878 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2879 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2880
2881 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2882 return -EINVAL;
2883 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2884 return -EINVAL;
2885 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2886 return -EINVAL;
2887 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2888 return -EINVAL;
2889 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2890 return -EINVAL;
2891
2892 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2893
2894 freq_range->start_freq_khz =
2895 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2896 freq_range->end_freq_khz =
2897 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2898 freq_range->max_bandwidth_khz =
2899 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2900
2901 power_rule->max_eirp =
2902 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2903
2904 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2905 power_rule->max_antenna_gain =
2906 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2907
2908 return 0;
2909}
2910
2911static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2912{
2913 int r;
2914 char *data = NULL;
2915
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002916 /*
2917 * You should only get this when cfg80211 hasn't yet initialized
2918 * completely when built-in to the kernel right between the time
2919 * window between nl80211_init() and regulatory_init(), if that is
2920 * even possible.
2921 */
2922 mutex_lock(&cfg80211_mutex);
2923 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002924 mutex_unlock(&cfg80211_mutex);
2925 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002926 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002927 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002928
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002929 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2930 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002931
2932 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2933
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002934 r = regulatory_hint_user(data);
2935
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002936 return r;
2937}
2938
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002939static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01002940 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002941{
Johannes Berg4c476992010-10-04 21:36:35 +02002942 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02002943 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01002944 struct wireless_dev *wdev = dev->ieee80211_ptr;
2945 struct mesh_config cur_params;
2946 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002947 void *hdr;
2948 struct nlattr *pinfoattr;
2949 struct sk_buff *msg;
2950
Johannes Berg29cbe682010-12-03 09:20:44 +01002951 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
2952 return -EOPNOTSUPP;
2953
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002954 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02002955 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02002956
Johannes Berg29cbe682010-12-03 09:20:44 +01002957 wdev_lock(wdev);
2958 /* If not connected, get default parameters */
2959 if (!wdev->mesh_id_len)
2960 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
2961 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002962 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01002963 &cur_params);
2964 wdev_unlock(wdev);
2965
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002966 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002967 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002968
2969 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002970 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002971 if (!msg)
2972 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002973 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002974 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002975 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01002976 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002977 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002978 if (!pinfoattr)
2979 goto nla_put_failure;
2980 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2981 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2982 cur_params.dot11MeshRetryTimeout);
2983 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2984 cur_params.dot11MeshConfirmTimeout);
2985 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2986 cur_params.dot11MeshHoldingTimeout);
2987 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2988 cur_params.dot11MeshMaxPeerLinks);
2989 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2990 cur_params.dot11MeshMaxRetries);
2991 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2992 cur_params.dot11MeshTTL);
Javier Cardona45904f22010-12-03 09:20:40 +01002993 NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL,
2994 cur_params.element_ttl);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002995 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2996 cur_params.auto_open_plinks);
2997 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2998 cur_params.dot11MeshHWMPmaxPREQretries);
2999 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3000 cur_params.path_refresh_time);
3001 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3002 cur_params.min_discovery_timeout);
3003 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3004 cur_params.dot11MeshHWMPactivePathTimeout);
3005 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3006 cur_params.dot11MeshHWMPpreqMinInterval);
3007 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3008 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00003009 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3010 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003011 nla_nest_end(msg, pinfoattr);
3012 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003013 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003014
Johannes Berg3b858752009-03-12 09:55:09 +01003015 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003016 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003017 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003018 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003019 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003020}
3021
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003022static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003023 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3024 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3025 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3026 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3027 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3028 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003029 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003030 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
3031
3032 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3033 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3034 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3035 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3036 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
3037 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
3038};
3039
Javier Cardonac80d5452010-12-16 17:37:49 -08003040static const struct nla_policy
3041 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
3042 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3043 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003044 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003045 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Javier Cardonac80d5452010-12-16 17:37:49 -08003046 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003047 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003048};
3049
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003050static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003051 struct mesh_config *cfg,
3052 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003053{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003054 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003055 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003056
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003057#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3058do {\
3059 if (table[attr_num]) {\
3060 cfg->param = nla_fn(table[attr_num]); \
3061 mask |= (1 << (attr_num - 1)); \
3062 } \
3063} while (0);\
3064
3065
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003066 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003067 return -EINVAL;
3068 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003069 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003070 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003071 return -EINVAL;
3072
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003073 /* This makes sure that there aren't more than 32 mesh config
3074 * parameters (otherwise our bitfield scheme would not work.) */
3075 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3076
3077 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003078 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
3079 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
3080 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
3081 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
3082 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
3083 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
3084 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
3085 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
3086 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
3087 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
3088 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
3089 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003090 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
3091 mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003092 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
3093 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
3094 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
3095 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3096 nla_get_u8);
3097 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
3098 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
3099 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
3100 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3101 nla_get_u16);
3102 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
3103 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3104 nla_get_u32);
3105 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
3106 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3107 nla_get_u16);
3108 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3109 dot11MeshHWMPnetDiameterTraversalTime,
3110 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3111 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003112 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3113 dot11MeshHWMPRootMode, mask,
3114 NL80211_MESHCONF_HWMP_ROOTMODE,
3115 nla_get_u8);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003116 if (mask_out)
3117 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003118
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003119 return 0;
3120
3121#undef FILL_IN_MESH_PARAM_IF_SET
3122}
3123
Javier Cardonac80d5452010-12-16 17:37:49 -08003124static int nl80211_parse_mesh_setup(struct genl_info *info,
3125 struct mesh_setup *setup)
3126{
3127 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3128
3129 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3130 return -EINVAL;
3131 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3132 info->attrs[NL80211_ATTR_MESH_SETUP],
3133 nl80211_mesh_setup_params_policy))
3134 return -EINVAL;
3135
3136 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3137 setup->path_sel_proto =
3138 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3139 IEEE80211_PATH_PROTOCOL_VENDOR :
3140 IEEE80211_PATH_PROTOCOL_HWMP;
3141
3142 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3143 setup->path_metric =
3144 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3145 IEEE80211_PATH_METRIC_VENDOR :
3146 IEEE80211_PATH_METRIC_AIRTIME;
3147
Javier Cardona581a8b02011-04-07 15:08:27 -07003148
3149 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003150 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003151 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003152 if (!is_valid_ie_attr(ieattr))
3153 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003154 setup->ie = nla_data(ieattr);
3155 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003156 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003157 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3158 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003159
3160 return 0;
3161}
3162
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003163static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003164 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003165{
3166 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3167 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003168 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003169 struct mesh_config cfg;
3170 u32 mask;
3171 int err;
3172
Johannes Berg29cbe682010-12-03 09:20:44 +01003173 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3174 return -EOPNOTSUPP;
3175
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003176 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003177 return -EOPNOTSUPP;
3178
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003179 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003180 if (err)
3181 return err;
3182
Johannes Berg29cbe682010-12-03 09:20:44 +01003183 wdev_lock(wdev);
3184 if (!wdev->mesh_id_len)
3185 err = -ENOLINK;
3186
3187 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003188 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003189 mask, &cfg);
3190
3191 wdev_unlock(wdev);
3192
3193 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003194}
3195
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003196static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3197{
3198 struct sk_buff *msg;
3199 void *hdr = NULL;
3200 struct nlattr *nl_reg_rules;
3201 unsigned int i;
3202 int err = -EINVAL;
3203
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003204 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003205
3206 if (!cfg80211_regdomain)
3207 goto out;
3208
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003209 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003210 if (!msg) {
3211 err = -ENOBUFS;
3212 goto out;
3213 }
3214
3215 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3216 NL80211_CMD_GET_REG);
3217 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003218 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003219
3220 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3221 cfg80211_regdomain->alpha2);
3222
3223 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3224 if (!nl_reg_rules)
3225 goto nla_put_failure;
3226
3227 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3228 struct nlattr *nl_reg_rule;
3229 const struct ieee80211_reg_rule *reg_rule;
3230 const struct ieee80211_freq_range *freq_range;
3231 const struct ieee80211_power_rule *power_rule;
3232
3233 reg_rule = &cfg80211_regdomain->reg_rules[i];
3234 freq_range = &reg_rule->freq_range;
3235 power_rule = &reg_rule->power_rule;
3236
3237 nl_reg_rule = nla_nest_start(msg, i);
3238 if (!nl_reg_rule)
3239 goto nla_put_failure;
3240
3241 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3242 reg_rule->flags);
3243 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3244 freq_range->start_freq_khz);
3245 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3246 freq_range->end_freq_khz);
3247 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3248 freq_range->max_bandwidth_khz);
3249 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3250 power_rule->max_antenna_gain);
3251 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3252 power_rule->max_eirp);
3253
3254 nla_nest_end(msg, nl_reg_rule);
3255 }
3256
3257 nla_nest_end(msg, nl_reg_rules);
3258
3259 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003260 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003261 goto out;
3262
3263nla_put_failure:
3264 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003265put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003266 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003267 err = -EMSGSIZE;
3268out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003269 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003270 return err;
3271}
3272
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003273static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3274{
3275 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3276 struct nlattr *nl_reg_rule;
3277 char *alpha2 = NULL;
3278 int rem_reg_rules = 0, r = 0;
3279 u32 num_rules = 0, rule_idx = 0, size_of_regd;
3280 struct ieee80211_regdomain *rd = NULL;
3281
3282 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3283 return -EINVAL;
3284
3285 if (!info->attrs[NL80211_ATTR_REG_RULES])
3286 return -EINVAL;
3287
3288 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3289
3290 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3291 rem_reg_rules) {
3292 num_rules++;
3293 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003294 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003295 }
3296
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003297 mutex_lock(&cfg80211_mutex);
3298
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003299 if (!reg_is_valid_request(alpha2)) {
3300 r = -EINVAL;
3301 goto bad_reg;
3302 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003303
3304 size_of_regd = sizeof(struct ieee80211_regdomain) +
3305 (num_rules * sizeof(struct ieee80211_reg_rule));
3306
3307 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003308 if (!rd) {
3309 r = -ENOMEM;
3310 goto bad_reg;
3311 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003312
3313 rd->n_reg_rules = num_rules;
3314 rd->alpha2[0] = alpha2[0];
3315 rd->alpha2[1] = alpha2[1];
3316
3317 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3318 rem_reg_rules) {
3319 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3320 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3321 reg_rule_policy);
3322 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3323 if (r)
3324 goto bad_reg;
3325
3326 rule_idx++;
3327
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003328 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3329 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003330 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003331 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003332 }
3333
3334 BUG_ON(rule_idx != num_rules);
3335
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003336 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003337
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003338 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003339
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003340 return r;
3341
Johannes Bergd2372b32008-10-24 20:32:20 +02003342 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003343 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003344 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003345 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003346}
3347
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003348static int validate_scan_freqs(struct nlattr *freqs)
3349{
3350 struct nlattr *attr1, *attr2;
3351 int n_channels = 0, tmp1, tmp2;
3352
3353 nla_for_each_nested(attr1, freqs, tmp1) {
3354 n_channels++;
3355 /*
3356 * Some hardware has a limited channel list for
3357 * scanning, and it is pretty much nonsensical
3358 * to scan for a channel twice, so disallow that
3359 * and don't require drivers to check that the
3360 * channel list they get isn't longer than what
3361 * they can scan, as long as they can scan all
3362 * the channels they registered at once.
3363 */
3364 nla_for_each_nested(attr2, freqs, tmp2)
3365 if (attr1 != attr2 &&
3366 nla_get_u32(attr1) == nla_get_u32(attr2))
3367 return 0;
3368 }
3369
3370 return n_channels;
3371}
3372
Johannes Berg2a519312009-02-10 21:25:55 +01003373static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3374{
Johannes Berg4c476992010-10-04 21:36:35 +02003375 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3376 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003377 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003378 struct nlattr *attr;
3379 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003380 int err, tmp, n_ssids = 0, n_channels, i;
Johannes Berg2a519312009-02-10 21:25:55 +01003381 enum ieee80211_band band;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003382 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003383
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003384 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3385 return -EINVAL;
3386
Johannes Berg79c97e92009-07-07 03:56:12 +02003387 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003388
Johannes Berg4c476992010-10-04 21:36:35 +02003389 if (!rdev->ops->scan)
3390 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003391
Johannes Berg4c476992010-10-04 21:36:35 +02003392 if (rdev->scan_req)
3393 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003394
3395 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003396 n_channels = validate_scan_freqs(
3397 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003398 if (!n_channels)
3399 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003400 } else {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003401 n_channels = 0;
3402
Johannes Berg2a519312009-02-10 21:25:55 +01003403 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3404 if (wiphy->bands[band])
3405 n_channels += wiphy->bands[band]->n_channels;
3406 }
3407
3408 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3409 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3410 n_ssids++;
3411
Johannes Berg4c476992010-10-04 21:36:35 +02003412 if (n_ssids > wiphy->max_scan_ssids)
3413 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003414
Jouni Malinen70692ad2009-02-16 19:39:13 +02003415 if (info->attrs[NL80211_ATTR_IE])
3416 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3417 else
3418 ie_len = 0;
3419
Johannes Berg4c476992010-10-04 21:36:35 +02003420 if (ie_len > wiphy->max_scan_ie_len)
3421 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003422
Johannes Berg2a519312009-02-10 21:25:55 +01003423 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003424 + sizeof(*request->ssids) * n_ssids
3425 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003426 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003427 if (!request)
3428 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003429
Johannes Berg2a519312009-02-10 21:25:55 +01003430 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003431 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003432 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003433 if (ie_len) {
3434 if (request->ssids)
3435 request->ie = (void *)(request->ssids + n_ssids);
3436 else
3437 request->ie = (void *)(request->channels + n_channels);
3438 }
Johannes Berg2a519312009-02-10 21:25:55 +01003439
Johannes Berg584991d2009-11-02 13:32:03 +01003440 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003441 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3442 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003443 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003444 struct ieee80211_channel *chan;
3445
3446 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3447
3448 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003449 err = -EINVAL;
3450 goto out_free;
3451 }
Johannes Berg584991d2009-11-02 13:32:03 +01003452
3453 /* ignore disabled channels */
3454 if (chan->flags & IEEE80211_CHAN_DISABLED)
3455 continue;
3456
3457 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003458 i++;
3459 }
3460 } else {
3461 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003462 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3463 int j;
3464 if (!wiphy->bands[band])
3465 continue;
3466 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003467 struct ieee80211_channel *chan;
3468
3469 chan = &wiphy->bands[band]->channels[j];
3470
3471 if (chan->flags & IEEE80211_CHAN_DISABLED)
3472 continue;
3473
3474 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003475 i++;
3476 }
3477 }
3478 }
3479
Johannes Berg584991d2009-11-02 13:32:03 +01003480 if (!i) {
3481 err = -EINVAL;
3482 goto out_free;
3483 }
3484
3485 request->n_channels = i;
3486
Johannes Berg2a519312009-02-10 21:25:55 +01003487 i = 0;
3488 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3489 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003490 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01003491 err = -EINVAL;
3492 goto out_free;
3493 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003494 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01003495 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01003496 i++;
3497 }
3498 }
3499
Jouni Malinen70692ad2009-02-16 19:39:13 +02003500 if (info->attrs[NL80211_ATTR_IE]) {
3501 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003502 memcpy((void *)request->ie,
3503 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003504 request->ie_len);
3505 }
3506
Johannes Berg463d0182009-07-14 00:33:35 +02003507 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003508 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003509
Johannes Berg79c97e92009-07-07 03:56:12 +02003510 rdev->scan_req = request;
3511 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003512
Johannes Berg463d0182009-07-14 00:33:35 +02003513 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003514 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003515 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02003516 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01003517 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02003518 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003519 kfree(request);
3520 }
Johannes Berg3b858752009-03-12 09:55:09 +01003521
Johannes Berg2a519312009-02-10 21:25:55 +01003522 return err;
3523}
3524
Luciano Coelho807f8a82011-05-11 17:09:35 +03003525static int nl80211_start_sched_scan(struct sk_buff *skb,
3526 struct genl_info *info)
3527{
3528 struct cfg80211_sched_scan_request *request;
3529 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3530 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003531 struct nlattr *attr;
3532 struct wiphy *wiphy;
3533 int err, tmp, n_ssids = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003534 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003535 enum ieee80211_band band;
3536 size_t ie_len;
3537
3538 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3539 !rdev->ops->sched_scan_start)
3540 return -EOPNOTSUPP;
3541
3542 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3543 return -EINVAL;
3544
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003545 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
3546 return -EINVAL;
3547
3548 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
3549 if (interval == 0)
3550 return -EINVAL;
3551
Luciano Coelho807f8a82011-05-11 17:09:35 +03003552 wiphy = &rdev->wiphy;
3553
3554 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3555 n_channels = validate_scan_freqs(
3556 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3557 if (!n_channels)
3558 return -EINVAL;
3559 } else {
3560 n_channels = 0;
3561
3562 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3563 if (wiphy->bands[band])
3564 n_channels += wiphy->bands[band]->n_channels;
3565 }
3566
3567 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3568 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3569 tmp)
3570 n_ssids++;
3571
3572 if (n_ssids > wiphy->max_scan_ssids)
3573 return -EINVAL;
3574
3575 if (info->attrs[NL80211_ATTR_IE])
3576 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3577 else
3578 ie_len = 0;
3579
3580 if (ie_len > wiphy->max_scan_ie_len)
3581 return -EINVAL;
3582
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003583 mutex_lock(&rdev->sched_scan_mtx);
3584
3585 if (rdev->sched_scan_req) {
3586 err = -EINPROGRESS;
3587 goto out;
3588 }
3589
Luciano Coelho807f8a82011-05-11 17:09:35 +03003590 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003591 + sizeof(*request->ssids) * n_ssids
3592 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03003593 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003594 if (!request) {
3595 err = -ENOMEM;
3596 goto out;
3597 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03003598
3599 if (n_ssids)
3600 request->ssids = (void *)&request->channels[n_channels];
3601 request->n_ssids = n_ssids;
3602 if (ie_len) {
3603 if (request->ssids)
3604 request->ie = (void *)(request->ssids + n_ssids);
3605 else
3606 request->ie = (void *)(request->channels + n_channels);
3607 }
3608
3609 i = 0;
3610 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3611 /* user specified, bail out if channel not found */
3612 nla_for_each_nested(attr,
3613 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
3614 tmp) {
3615 struct ieee80211_channel *chan;
3616
3617 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3618
3619 if (!chan) {
3620 err = -EINVAL;
3621 goto out_free;
3622 }
3623
3624 /* ignore disabled channels */
3625 if (chan->flags & IEEE80211_CHAN_DISABLED)
3626 continue;
3627
3628 request->channels[i] = chan;
3629 i++;
3630 }
3631 } else {
3632 /* all channels */
3633 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3634 int j;
3635 if (!wiphy->bands[band])
3636 continue;
3637 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3638 struct ieee80211_channel *chan;
3639
3640 chan = &wiphy->bands[band]->channels[j];
3641
3642 if (chan->flags & IEEE80211_CHAN_DISABLED)
3643 continue;
3644
3645 request->channels[i] = chan;
3646 i++;
3647 }
3648 }
3649 }
3650
3651 if (!i) {
3652 err = -EINVAL;
3653 goto out_free;
3654 }
3655
3656 request->n_channels = i;
3657
3658 i = 0;
3659 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3660 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3661 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003662 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03003663 err = -EINVAL;
3664 goto out_free;
3665 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003666 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003667 memcpy(request->ssids[i].ssid, nla_data(attr),
3668 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03003669 i++;
3670 }
3671 }
3672
3673 if (info->attrs[NL80211_ATTR_IE]) {
3674 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3675 memcpy((void *)request->ie,
3676 nla_data(info->attrs[NL80211_ATTR_IE]),
3677 request->ie_len);
3678 }
3679
3680 request->dev = dev;
3681 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003682 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003683
3684 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
3685 if (!err) {
3686 rdev->sched_scan_req = request;
3687 nl80211_send_sched_scan(rdev, dev,
3688 NL80211_CMD_START_SCHED_SCAN);
3689 goto out;
3690 }
3691
3692out_free:
3693 kfree(request);
3694out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003695 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003696 return err;
3697}
3698
3699static int nl80211_stop_sched_scan(struct sk_buff *skb,
3700 struct genl_info *info)
3701{
3702 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003703 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003704
3705 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3706 !rdev->ops->sched_scan_stop)
3707 return -EOPNOTSUPP;
3708
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003709 mutex_lock(&rdev->sched_scan_mtx);
3710 err = __cfg80211_stop_sched_scan(rdev, false);
3711 mutex_unlock(&rdev->sched_scan_mtx);
3712
3713 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003714}
3715
Johannes Berg2a519312009-02-10 21:25:55 +01003716static int nl80211_send_bss(struct sk_buff *msg, u32 pid, u32 seq, int flags,
3717 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003718 struct wireless_dev *wdev,
3719 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003720{
Johannes Berg48ab9052009-07-10 18:42:31 +02003721 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003722 void *hdr;
3723 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003724 int i;
3725
3726 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003727
3728 hdr = nl80211hdr_put(msg, pid, seq, flags,
3729 NL80211_CMD_NEW_SCAN_RESULTS);
3730 if (!hdr)
3731 return -1;
3732
Johannes Bergf5ea9122009-08-07 16:17:38 +02003733 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003734 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003735
3736 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3737 if (!bss)
3738 goto nla_put_failure;
3739 if (!is_zero_ether_addr(res->bssid))
3740 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3741 if (res->information_elements && res->len_information_elements)
3742 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3743 res->len_information_elements,
3744 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02003745 if (res->beacon_ies && res->len_beacon_ies &&
3746 res->beacon_ies != res->information_elements)
3747 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
3748 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01003749 if (res->tsf)
3750 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3751 if (res->beacon_interval)
3752 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3753 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3754 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003755 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3756 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003757
Johannes Berg77965c92009-02-18 18:45:06 +01003758 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003759 case CFG80211_SIGNAL_TYPE_MBM:
3760 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3761 break;
3762 case CFG80211_SIGNAL_TYPE_UNSPEC:
3763 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3764 break;
3765 default:
3766 break;
3767 }
3768
Johannes Berg48ab9052009-07-10 18:42:31 +02003769 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02003770 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02003771 case NL80211_IFTYPE_STATION:
3772 if (intbss == wdev->current_bss)
3773 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3774 NL80211_BSS_STATUS_ASSOCIATED);
3775 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3776 if (intbss != wdev->auth_bsses[i])
3777 continue;
3778 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3779 NL80211_BSS_STATUS_AUTHENTICATED);
3780 break;
3781 }
3782 break;
3783 case NL80211_IFTYPE_ADHOC:
3784 if (intbss == wdev->current_bss)
3785 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3786 NL80211_BSS_STATUS_IBSS_JOINED);
3787 break;
3788 default:
3789 break;
3790 }
3791
Johannes Berg2a519312009-02-10 21:25:55 +01003792 nla_nest_end(msg, bss);
3793
3794 return genlmsg_end(msg, hdr);
3795
3796 nla_put_failure:
3797 genlmsg_cancel(msg, hdr);
3798 return -EMSGSIZE;
3799}
3800
3801static int nl80211_dump_scan(struct sk_buff *skb,
3802 struct netlink_callback *cb)
3803{
Johannes Berg48ab9052009-07-10 18:42:31 +02003804 struct cfg80211_registered_device *rdev;
3805 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003806 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003807 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003808 int start = cb->args[1], idx = 0;
3809 int err;
3810
Johannes Berg67748892010-10-04 21:14:06 +02003811 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
3812 if (err)
3813 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01003814
Johannes Berg48ab9052009-07-10 18:42:31 +02003815 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003816
Johannes Berg48ab9052009-07-10 18:42:31 +02003817 wdev_lock(wdev);
3818 spin_lock_bh(&rdev->bss_lock);
3819 cfg80211_bss_expire(rdev);
3820
3821 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003822 if (++idx <= start)
3823 continue;
3824 if (nl80211_send_bss(skb,
3825 NETLINK_CB(cb->skb).pid,
3826 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003827 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003828 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02003829 break;
Johannes Berg2a519312009-02-10 21:25:55 +01003830 }
3831 }
3832
Johannes Berg48ab9052009-07-10 18:42:31 +02003833 spin_unlock_bh(&rdev->bss_lock);
3834 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003835
3836 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02003837 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003838
Johannes Berg67748892010-10-04 21:14:06 +02003839 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01003840}
3841
Holger Schurig61fa7132009-11-11 12:25:40 +01003842static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3843 int flags, struct net_device *dev,
3844 struct survey_info *survey)
3845{
3846 void *hdr;
3847 struct nlattr *infoattr;
3848
3849 /* Survey without a channel doesn't make sense */
3850 if (!survey->channel)
3851 return -EINVAL;
3852
3853 hdr = nl80211hdr_put(msg, pid, seq, flags,
3854 NL80211_CMD_NEW_SURVEY_RESULTS);
3855 if (!hdr)
3856 return -ENOMEM;
3857
3858 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3859
3860 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3861 if (!infoattr)
3862 goto nla_put_failure;
3863
3864 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3865 survey->channel->center_freq);
3866 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3867 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3868 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02003869 if (survey->filled & SURVEY_INFO_IN_USE)
3870 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Felix Fietkau8610c29a2010-10-09 02:39:29 +02003871 if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
3872 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
3873 survey->channel_time);
3874 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
3875 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
3876 survey->channel_time_busy);
3877 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
3878 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
3879 survey->channel_time_ext_busy);
3880 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
3881 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
3882 survey->channel_time_rx);
3883 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
3884 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
3885 survey->channel_time_tx);
Holger Schurig61fa7132009-11-11 12:25:40 +01003886
3887 nla_nest_end(msg, infoattr);
3888
3889 return genlmsg_end(msg, hdr);
3890
3891 nla_put_failure:
3892 genlmsg_cancel(msg, hdr);
3893 return -EMSGSIZE;
3894}
3895
3896static int nl80211_dump_survey(struct sk_buff *skb,
3897 struct netlink_callback *cb)
3898{
3899 struct survey_info survey;
3900 struct cfg80211_registered_device *dev;
3901 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01003902 int survey_idx = cb->args[1];
3903 int res;
3904
Johannes Berg67748892010-10-04 21:14:06 +02003905 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3906 if (res)
3907 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01003908
3909 if (!dev->ops->dump_survey) {
3910 res = -EOPNOTSUPP;
3911 goto out_err;
3912 }
3913
3914 while (1) {
3915 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3916 &survey);
3917 if (res == -ENOENT)
3918 break;
3919 if (res)
3920 goto out_err;
3921
3922 if (nl80211_send_survey(skb,
3923 NETLINK_CB(cb->skb).pid,
3924 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3925 netdev,
3926 &survey) < 0)
3927 goto out;
3928 survey_idx++;
3929 }
3930
3931 out:
3932 cb->args[1] = survey_idx;
3933 res = skb->len;
3934 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003935 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01003936 return res;
3937}
3938
Jouni Malinen255e7372009-03-20 21:21:17 +02003939static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3940{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003941 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003942}
3943
Samuel Ortizb23aa672009-07-01 21:26:54 +02003944static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3945{
3946 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3947 NL80211_WPA_VERSION_2));
3948}
3949
3950static bool nl80211_valid_akm_suite(u32 akm)
3951{
3952 return akm == WLAN_AKM_SUITE_8021X ||
3953 akm == WLAN_AKM_SUITE_PSK;
3954}
3955
3956static bool nl80211_valid_cipher_suite(u32 cipher)
3957{
3958 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3959 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3960 cipher == WLAN_CIPHER_SUITE_TKIP ||
3961 cipher == WLAN_CIPHER_SUITE_CCMP ||
Deepthi Gowri5b26a952011-11-29 11:22:42 +05303962 cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
3963 cipher == WLAN_CIPHER_SUITE_SMS4;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003964}
3965
3966
Jouni Malinen636a5d32009-03-19 13:39:22 +02003967static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3968{
Johannes Berg4c476992010-10-04 21:36:35 +02003969 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3970 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003971 struct ieee80211_channel *chan;
3972 const u8 *bssid, *ssid, *ie = NULL;
3973 int err, ssid_len, ie_len = 0;
3974 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003975 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003976 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003977
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003978 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3979 return -EINVAL;
3980
3981 if (!info->attrs[NL80211_ATTR_MAC])
3982 return -EINVAL;
3983
Jouni Malinen17780922009-03-27 20:52:47 +02003984 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3985 return -EINVAL;
3986
Johannes Berg19957bb2009-07-02 17:20:43 +02003987 if (!info->attrs[NL80211_ATTR_SSID])
3988 return -EINVAL;
3989
3990 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3991 return -EINVAL;
3992
Johannes Bergfffd0932009-07-08 14:22:54 +02003993 err = nl80211_parse_key(info, &key);
3994 if (err)
3995 return err;
3996
3997 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02003998 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
3999 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004000 if (!key.p.key || !key.p.key_len)
4001 return -EINVAL;
4002 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4003 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4004 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4005 key.p.key_len != WLAN_KEY_LEN_WEP104))
4006 return -EINVAL;
4007 if (key.idx > 4)
4008 return -EINVAL;
4009 } else {
4010 key.p.key_len = 0;
4011 key.p.key = NULL;
4012 }
4013
Johannes Bergafea0b72010-08-10 09:46:42 +02004014 if (key.idx >= 0) {
4015 int i;
4016 bool ok = false;
4017 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4018 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4019 ok = true;
4020 break;
4021 }
4022 }
Johannes Berg4c476992010-10-04 21:36:35 +02004023 if (!ok)
4024 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004025 }
4026
Johannes Berg4c476992010-10-04 21:36:35 +02004027 if (!rdev->ops->auth)
4028 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004029
Johannes Berg074ac8d2010-09-16 14:58:22 +02004030 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004031 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4032 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004033
Johannes Berg19957bb2009-07-02 17:20:43 +02004034 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004035 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004036 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004037 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4038 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004039
Johannes Berg19957bb2009-07-02 17:20:43 +02004040 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4041 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4042
4043 if (info->attrs[NL80211_ATTR_IE]) {
4044 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4045 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4046 }
4047
4048 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004049 if (!nl80211_valid_auth_type(auth_type))
4050 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004051
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004052 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4053
Johannes Berg4c476992010-10-04 21:36:35 +02004054 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4055 ssid, ssid_len, ie, ie_len,
4056 key.p.key, key.p.key_len, key.idx,
4057 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004058}
4059
Johannes Bergc0692b82010-08-27 14:26:53 +03004060static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4061 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004062 struct cfg80211_crypto_settings *settings,
4063 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004064{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004065 memset(settings, 0, sizeof(*settings));
4066
Samuel Ortizb23aa672009-07-01 21:26:54 +02004067 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4068
Johannes Bergc0692b82010-08-27 14:26:53 +03004069 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4070 u16 proto;
4071 proto = nla_get_u16(
4072 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4073 settings->control_port_ethertype = cpu_to_be16(proto);
4074 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4075 proto != ETH_P_PAE)
4076 return -EINVAL;
4077 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4078 settings->control_port_no_encrypt = true;
4079 } else
4080 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4081
Samuel Ortizb23aa672009-07-01 21:26:54 +02004082 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4083 void *data;
4084 int len, i;
4085
4086 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4087 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4088 settings->n_ciphers_pairwise = len / sizeof(u32);
4089
4090 if (len % sizeof(u32))
4091 return -EINVAL;
4092
Johannes Berg3dc27d22009-07-02 21:36:37 +02004093 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004094 return -EINVAL;
4095
4096 memcpy(settings->ciphers_pairwise, data, len);
4097
4098 for (i = 0; i < settings->n_ciphers_pairwise; i++)
4099 if (!nl80211_valid_cipher_suite(
4100 settings->ciphers_pairwise[i]))
4101 return -EINVAL;
4102 }
4103
4104 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4105 settings->cipher_group =
4106 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
4107 if (!nl80211_valid_cipher_suite(settings->cipher_group))
4108 return -EINVAL;
4109 }
4110
4111 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4112 settings->wpa_versions =
4113 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4114 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4115 return -EINVAL;
4116 }
4117
4118 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4119 void *data;
4120 int len, i;
4121
4122 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4123 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4124 settings->n_akm_suites = len / sizeof(u32);
4125
4126 if (len % sizeof(u32))
4127 return -EINVAL;
4128
Jouni Malinen508ed742011-09-21 16:13:07 +03004129 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4130 return -EINVAL;
4131
Samuel Ortizb23aa672009-07-01 21:26:54 +02004132 memcpy(settings->akm_suites, data, len);
4133
Jouni Malinen508ed742011-09-21 16:13:07 +03004134 for (i = 0; i < settings->n_akm_suites; i++)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004135 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
4136 return -EINVAL;
4137 }
4138
4139 return 0;
4140}
4141
Jouni Malinen636a5d32009-03-19 13:39:22 +02004142static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4143{
Johannes Berg4c476992010-10-04 21:36:35 +02004144 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4145 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004146 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004147 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004148 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004149 int err, ssid_len, ie_len = 0;
4150 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004151
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004152 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4153 return -EINVAL;
4154
4155 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004156 !info->attrs[NL80211_ATTR_SSID] ||
4157 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004158 return -EINVAL;
4159
Johannes Berg4c476992010-10-04 21:36:35 +02004160 if (!rdev->ops->assoc)
4161 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004162
Johannes Berg074ac8d2010-09-16 14:58:22 +02004163 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004164 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4165 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004166
Johannes Berg19957bb2009-07-02 17:20:43 +02004167 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004168
Johannes Berg19957bb2009-07-02 17:20:43 +02004169 chan = ieee80211_get_channel(&rdev->wiphy,
4170 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004171 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4172 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004173
Johannes Berg19957bb2009-07-02 17:20:43 +02004174 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4175 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004176
4177 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004178 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4179 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004180 }
4181
Jouni Malinendc6382c2009-05-06 22:09:37 +03004182 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004183 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004184 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004185 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004186 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004187 else if (mfp != NL80211_MFP_NO)
4188 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004189 }
4190
Johannes Berg3e5d7642009-07-07 14:37:26 +02004191 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4192 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4193
Johannes Bergc0692b82010-08-27 14:26:53 +03004194 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004195 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004196 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4197 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02004198 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004199
Jouni Malinen636a5d32009-03-19 13:39:22 +02004200 return err;
4201}
4202
4203static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4204{
Johannes Berg4c476992010-10-04 21:36:35 +02004205 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4206 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004207 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004208 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004209 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004210 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004211
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004212 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4213 return -EINVAL;
4214
4215 if (!info->attrs[NL80211_ATTR_MAC])
4216 return -EINVAL;
4217
4218 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4219 return -EINVAL;
4220
Johannes Berg4c476992010-10-04 21:36:35 +02004221 if (!rdev->ops->deauth)
4222 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004223
Johannes Berg074ac8d2010-09-16 14:58:22 +02004224 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004225 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4226 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004227
Johannes Berg19957bb2009-07-02 17:20:43 +02004228 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004229
Johannes Berg19957bb2009-07-02 17:20:43 +02004230 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4231 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004232 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004233 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004234 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004235
4236 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004237 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4238 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004239 }
4240
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004241 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4242
Johannes Berg4c476992010-10-04 21:36:35 +02004243 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4244 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004245}
4246
4247static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4248{
Johannes Berg4c476992010-10-04 21:36:35 +02004249 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4250 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004251 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004252 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004253 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004254 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004255
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004256 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4257 return -EINVAL;
4258
4259 if (!info->attrs[NL80211_ATTR_MAC])
4260 return -EINVAL;
4261
4262 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4263 return -EINVAL;
4264
Johannes Berg4c476992010-10-04 21:36:35 +02004265 if (!rdev->ops->disassoc)
4266 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004267
Johannes Berg074ac8d2010-09-16 14:58:22 +02004268 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004269 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4270 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004271
Johannes Berg19957bb2009-07-02 17:20:43 +02004272 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004273
Johannes Berg19957bb2009-07-02 17:20:43 +02004274 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4275 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004276 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004277 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004278 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004279
4280 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004281 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4282 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004283 }
4284
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004285 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4286
Johannes Berg4c476992010-10-04 21:36:35 +02004287 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4288 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004289}
4290
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004291static bool
4292nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4293 int mcast_rate[IEEE80211_NUM_BANDS],
4294 int rateval)
4295{
4296 struct wiphy *wiphy = &rdev->wiphy;
4297 bool found = false;
4298 int band, i;
4299
4300 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4301 struct ieee80211_supported_band *sband;
4302
4303 sband = wiphy->bands[band];
4304 if (!sband)
4305 continue;
4306
4307 for (i = 0; i < sband->n_bitrates; i++) {
4308 if (sband->bitrates[i].bitrate == rateval) {
4309 mcast_rate[band] = i + 1;
4310 found = true;
4311 break;
4312 }
4313 }
4314 }
4315
4316 return found;
4317}
4318
Johannes Berg04a773a2009-04-19 21:24:32 +02004319static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4320{
Johannes Berg4c476992010-10-04 21:36:35 +02004321 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4322 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004323 struct cfg80211_ibss_params ibss;
4324 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004325 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004326 int err;
4327
Johannes Berg8e30bc52009-04-22 17:45:38 +02004328 memset(&ibss, 0, sizeof(ibss));
4329
Johannes Berg04a773a2009-04-19 21:24:32 +02004330 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4331 return -EINVAL;
4332
4333 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4334 !info->attrs[NL80211_ATTR_SSID] ||
4335 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4336 return -EINVAL;
4337
Johannes Berg8e30bc52009-04-22 17:45:38 +02004338 ibss.beacon_interval = 100;
4339
4340 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4341 ibss.beacon_interval =
4342 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4343 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4344 return -EINVAL;
4345 }
4346
Johannes Berg4c476992010-10-04 21:36:35 +02004347 if (!rdev->ops->join_ibss)
4348 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004349
Johannes Berg4c476992010-10-04 21:36:35 +02004350 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4351 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004352
Johannes Berg79c97e92009-07-07 03:56:12 +02004353 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004354
4355 if (info->attrs[NL80211_ATTR_MAC])
4356 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4357 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4358 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4359
4360 if (info->attrs[NL80211_ATTR_IE]) {
4361 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4362 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4363 }
4364
4365 ibss.channel = ieee80211_get_channel(wiphy,
4366 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4367 if (!ibss.channel ||
4368 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004369 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4370 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004371
4372 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004373 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004374
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004375 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4376 u8 *rates =
4377 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4378 int n_rates =
4379 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4380 struct ieee80211_supported_band *sband =
4381 wiphy->bands[ibss.channel->band];
4382 int i, j;
4383
Johannes Berg4c476992010-10-04 21:36:35 +02004384 if (n_rates == 0)
4385 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004386
4387 for (i = 0; i < n_rates; i++) {
4388 int rate = (rates[i] & 0x7f) * 5;
4389 bool found = false;
4390
4391 for (j = 0; j < sband->n_bitrates; j++) {
4392 if (sband->bitrates[j].bitrate == rate) {
4393 found = true;
4394 ibss.basic_rates |= BIT(j);
4395 break;
4396 }
4397 }
Johannes Berg4c476992010-10-04 21:36:35 +02004398 if (!found)
4399 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004400 }
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004401 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004402
4403 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4404 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4405 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4406 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004407
Johannes Berg4c476992010-10-04 21:36:35 +02004408 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4409 connkeys = nl80211_parse_connkeys(rdev,
4410 info->attrs[NL80211_ATTR_KEYS]);
4411 if (IS_ERR(connkeys))
4412 return PTR_ERR(connkeys);
4413 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004414
Johannes Berg4c476992010-10-04 21:36:35 +02004415 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004416 if (err)
4417 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004418 return err;
4419}
4420
4421static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4422{
Johannes Berg4c476992010-10-04 21:36:35 +02004423 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4424 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004425
Johannes Berg4c476992010-10-04 21:36:35 +02004426 if (!rdev->ops->leave_ibss)
4427 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004428
Johannes Berg4c476992010-10-04 21:36:35 +02004429 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4430 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004431
Johannes Berg4c476992010-10-04 21:36:35 +02004432 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004433}
4434
Johannes Bergaff89a92009-07-01 21:26:51 +02004435#ifdef CONFIG_NL80211_TESTMODE
4436static struct genl_multicast_group nl80211_testmode_mcgrp = {
4437 .name = "testmode",
4438};
4439
4440static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4441{
Johannes Berg4c476992010-10-04 21:36:35 +02004442 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02004443 int err;
4444
4445 if (!info->attrs[NL80211_ATTR_TESTDATA])
4446 return -EINVAL;
4447
Johannes Bergaff89a92009-07-01 21:26:51 +02004448 err = -EOPNOTSUPP;
4449 if (rdev->ops->testmode_cmd) {
4450 rdev->testmode_info = info;
4451 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4452 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4453 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4454 rdev->testmode_info = NULL;
4455 }
4456
Johannes Bergaff89a92009-07-01 21:26:51 +02004457 return err;
4458}
4459
4460static struct sk_buff *
4461__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
4462 int approxlen, u32 pid, u32 seq, gfp_t gfp)
4463{
4464 struct sk_buff *skb;
4465 void *hdr;
4466 struct nlattr *data;
4467
4468 skb = nlmsg_new(approxlen + 100, gfp);
4469 if (!skb)
4470 return NULL;
4471
4472 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
4473 if (!hdr) {
4474 kfree_skb(skb);
4475 return NULL;
4476 }
4477
4478 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4479 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4480
4481 ((void **)skb->cb)[0] = rdev;
4482 ((void **)skb->cb)[1] = hdr;
4483 ((void **)skb->cb)[2] = data;
4484
4485 return skb;
4486
4487 nla_put_failure:
4488 kfree_skb(skb);
4489 return NULL;
4490}
4491
4492struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
4493 int approxlen)
4494{
4495 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4496
4497 if (WARN_ON(!rdev->testmode_info))
4498 return NULL;
4499
4500 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
4501 rdev->testmode_info->snd_pid,
4502 rdev->testmode_info->snd_seq,
4503 GFP_KERNEL);
4504}
4505EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4506
4507int cfg80211_testmode_reply(struct sk_buff *skb)
4508{
4509 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4510 void *hdr = ((void **)skb->cb)[1];
4511 struct nlattr *data = ((void **)skb->cb)[2];
4512
4513 if (WARN_ON(!rdev->testmode_info)) {
4514 kfree_skb(skb);
4515 return -EINVAL;
4516 }
4517
4518 nla_nest_end(skb, data);
4519 genlmsg_end(skb, hdr);
4520 return genlmsg_reply(skb, rdev->testmode_info);
4521}
4522EXPORT_SYMBOL(cfg80211_testmode_reply);
4523
4524struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4525 int approxlen, gfp_t gfp)
4526{
4527 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4528
4529 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4530}
4531EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4532
4533void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4534{
4535 void *hdr = ((void **)skb->cb)[1];
4536 struct nlattr *data = ((void **)skb->cb)[2];
4537
4538 nla_nest_end(skb, data);
4539 genlmsg_end(skb, hdr);
4540 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4541}
4542EXPORT_SYMBOL(cfg80211_testmode_event);
4543#endif
4544
Samuel Ortizb23aa672009-07-01 21:26:54 +02004545static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4546{
Johannes Berg4c476992010-10-04 21:36:35 +02004547 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4548 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004549 struct cfg80211_connect_params connect;
4550 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004551 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004552 int err;
4553
4554 memset(&connect, 0, sizeof(connect));
4555
4556 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4557 return -EINVAL;
4558
4559 if (!info->attrs[NL80211_ATTR_SSID] ||
4560 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4561 return -EINVAL;
4562
4563 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4564 connect.auth_type =
4565 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4566 if (!nl80211_valid_auth_type(connect.auth_type))
4567 return -EINVAL;
4568 } else
4569 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4570
4571 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4572
Johannes Bergc0692b82010-08-27 14:26:53 +03004573 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004574 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004575 if (err)
4576 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004577
Johannes Berg074ac8d2010-09-16 14:58:22 +02004578 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004579 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4580 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004581
Johannes Berg79c97e92009-07-07 03:56:12 +02004582 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004583
Samuel Ortizb23aa672009-07-01 21:26:54 +02004584 if (info->attrs[NL80211_ATTR_MAC])
4585 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4586 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4587 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4588
4589 if (info->attrs[NL80211_ATTR_IE]) {
4590 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4591 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4592 }
4593
4594 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4595 connect.channel =
4596 ieee80211_get_channel(wiphy,
4597 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4598 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02004599 connect.channel->flags & IEEE80211_CHAN_DISABLED)
4600 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004601 }
4602
Johannes Bergfffd0932009-07-08 14:22:54 +02004603 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4604 connkeys = nl80211_parse_connkeys(rdev,
4605 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02004606 if (IS_ERR(connkeys))
4607 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004608 }
4609
4610 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004611 if (err)
4612 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004613 return err;
4614}
4615
4616static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4617{
Johannes Berg4c476992010-10-04 21:36:35 +02004618 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4619 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004620 u16 reason;
4621
4622 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4623 reason = WLAN_REASON_DEAUTH_LEAVING;
4624 else
4625 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4626
4627 if (reason == 0)
4628 return -EINVAL;
4629
Johannes Berg074ac8d2010-09-16 14:58:22 +02004630 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004631 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4632 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004633
Johannes Berg4c476992010-10-04 21:36:35 +02004634 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004635}
4636
Johannes Berg463d0182009-07-14 00:33:35 +02004637static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4638{
Johannes Berg4c476992010-10-04 21:36:35 +02004639 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02004640 struct net *net;
4641 int err;
4642 u32 pid;
4643
4644 if (!info->attrs[NL80211_ATTR_PID])
4645 return -EINVAL;
4646
4647 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4648
Johannes Berg463d0182009-07-14 00:33:35 +02004649 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02004650 if (IS_ERR(net))
4651 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004652
4653 err = 0;
4654
4655 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02004656 if (!net_eq(wiphy_net(&rdev->wiphy), net))
4657 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02004658
Johannes Berg463d0182009-07-14 00:33:35 +02004659 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004660 return err;
4661}
4662
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004663static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4664{
Johannes Berg4c476992010-10-04 21:36:35 +02004665 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004666 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4667 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02004668 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004669 struct cfg80211_pmksa pmksa;
4670
4671 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4672
4673 if (!info->attrs[NL80211_ATTR_MAC])
4674 return -EINVAL;
4675
4676 if (!info->attrs[NL80211_ATTR_PMKID])
4677 return -EINVAL;
4678
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004679 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4680 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4681
Johannes Berg074ac8d2010-09-16 14:58:22 +02004682 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004683 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4684 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004685
4686 switch (info->genlhdr->cmd) {
4687 case NL80211_CMD_SET_PMKSA:
4688 rdev_ops = rdev->ops->set_pmksa;
4689 break;
4690 case NL80211_CMD_DEL_PMKSA:
4691 rdev_ops = rdev->ops->del_pmksa;
4692 break;
4693 default:
4694 WARN_ON(1);
4695 break;
4696 }
4697
Johannes Berg4c476992010-10-04 21:36:35 +02004698 if (!rdev_ops)
4699 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004700
Johannes Berg4c476992010-10-04 21:36:35 +02004701 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004702}
4703
4704static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
4705{
Johannes Berg4c476992010-10-04 21:36:35 +02004706 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4707 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004708
Johannes Berg074ac8d2010-09-16 14:58:22 +02004709 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004710 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4711 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004712
Johannes Berg4c476992010-10-04 21:36:35 +02004713 if (!rdev->ops->flush_pmksa)
4714 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004715
Johannes Berg4c476992010-10-04 21:36:35 +02004716 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004717}
4718
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004719static int nl80211_remain_on_channel(struct sk_buff *skb,
4720 struct genl_info *info)
4721{
Johannes Berg4c476992010-10-04 21:36:35 +02004722 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4723 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004724 struct ieee80211_channel *chan;
4725 struct sk_buff *msg;
4726 void *hdr;
4727 u64 cookie;
4728 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4729 u32 freq, duration;
4730 int err;
4731
4732 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4733 !info->attrs[NL80211_ATTR_DURATION])
4734 return -EINVAL;
4735
4736 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4737
4738 /*
4739 * We should be on that channel for at least one jiffie,
4740 * and more than 5 seconds seems excessive.
4741 */
Johannes Berga2939112010-12-14 17:54:28 +01004742 if (!duration || !msecs_to_jiffies(duration) ||
4743 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004744 return -EINVAL;
4745
Johannes Berg4c476992010-10-04 21:36:35 +02004746 if (!rdev->ops->remain_on_channel)
4747 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004748
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004749 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4750 channel_type = nla_get_u32(
4751 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4752 if (channel_type != NL80211_CHAN_NO_HT &&
4753 channel_type != NL80211_CHAN_HT20 &&
4754 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02004755 channel_type != NL80211_CHAN_HT40MINUS)
4756 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004757 }
4758
4759 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4760 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02004761 if (chan == NULL)
4762 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004763
4764 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004765 if (!msg)
4766 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004767
4768 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4769 NL80211_CMD_REMAIN_ON_CHANNEL);
4770
4771 if (IS_ERR(hdr)) {
4772 err = PTR_ERR(hdr);
4773 goto free_msg;
4774 }
4775
4776 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
4777 channel_type, duration, &cookie);
4778
4779 if (err)
4780 goto free_msg;
4781
4782 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4783
4784 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004785
4786 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004787
4788 nla_put_failure:
4789 err = -ENOBUFS;
4790 free_msg:
4791 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004792 return err;
4793}
4794
4795static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4796 struct genl_info *info)
4797{
Johannes Berg4c476992010-10-04 21:36:35 +02004798 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4799 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004800 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004801
4802 if (!info->attrs[NL80211_ATTR_COOKIE])
4803 return -EINVAL;
4804
Johannes Berg4c476992010-10-04 21:36:35 +02004805 if (!rdev->ops->cancel_remain_on_channel)
4806 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004807
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004808 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4809
Johannes Berg4c476992010-10-04 21:36:35 +02004810 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004811}
4812
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004813static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4814 u8 *rates, u8 rates_len)
4815{
4816 u8 i;
4817 u32 mask = 0;
4818
4819 for (i = 0; i < rates_len; i++) {
4820 int rate = (rates[i] & 0x7f) * 5;
4821 int ridx;
4822 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4823 struct ieee80211_rate *srate =
4824 &sband->bitrates[ridx];
4825 if (rate == srate->bitrate) {
4826 mask |= 1 << ridx;
4827 break;
4828 }
4829 }
4830 if (ridx == sband->n_bitrates)
4831 return 0; /* rate not found */
4832 }
4833
4834 return mask;
4835}
4836
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004837static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004838 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4839 .len = NL80211_MAX_SUPP_RATES },
4840};
4841
4842static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4843 struct genl_info *info)
4844{
4845 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02004846 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004847 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02004848 int rem, i;
4849 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004850 struct nlattr *tx_rates;
4851 struct ieee80211_supported_band *sband;
4852
4853 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4854 return -EINVAL;
4855
Johannes Berg4c476992010-10-04 21:36:35 +02004856 if (!rdev->ops->set_bitrate_mask)
4857 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004858
4859 memset(&mask, 0, sizeof(mask));
4860 /* Default to all rates enabled */
4861 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4862 sband = rdev->wiphy.bands[i];
4863 mask.control[i].legacy =
4864 sband ? (1 << sband->n_bitrates) - 1 : 0;
4865 }
4866
4867 /*
4868 * The nested attribute uses enum nl80211_band as the index. This maps
4869 * directly to the enum ieee80211_band values used in cfg80211.
4870 */
4871 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4872 {
4873 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02004874 if (band < 0 || band >= IEEE80211_NUM_BANDS)
4875 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004876 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02004877 if (sband == NULL)
4878 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004879 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4880 nla_len(tx_rates), nl80211_txattr_policy);
4881 if (tb[NL80211_TXRATE_LEGACY]) {
4882 mask.control[band].legacy = rateset_to_mask(
4883 sband,
4884 nla_data(tb[NL80211_TXRATE_LEGACY]),
4885 nla_len(tb[NL80211_TXRATE_LEGACY]));
Johannes Berg4c476992010-10-04 21:36:35 +02004886 if (mask.control[band].legacy == 0)
4887 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004888 }
4889 }
4890
Johannes Berg4c476992010-10-04 21:36:35 +02004891 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004892}
4893
Johannes Berg2e161f72010-08-12 15:38:38 +02004894static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004895{
Johannes Berg4c476992010-10-04 21:36:35 +02004896 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4897 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02004898 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02004899
4900 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4901 return -EINVAL;
4902
Johannes Berg2e161f72010-08-12 15:38:38 +02004903 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
4904 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02004905
Johannes Berg9d38d852010-06-09 17:20:33 +02004906 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004907 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004908 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4909 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4910 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08004911 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02004912 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4913 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004914
4915 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02004916 if (!rdev->ops->mgmt_tx)
4917 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004918
Johannes Berg4c476992010-10-04 21:36:35 +02004919 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02004920 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02004921 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
4922 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02004923}
4924
Johannes Berg2e161f72010-08-12 15:38:38 +02004925static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004926{
Johannes Berg4c476992010-10-04 21:36:35 +02004927 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4928 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02004929 struct ieee80211_channel *chan;
4930 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02004931 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02004932 u32 freq;
4933 int err;
4934 void *hdr;
4935 u64 cookie;
4936 struct sk_buff *msg;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004937 unsigned int wait = 0;
4938 bool offchan;
Jouni Malinen026331c2010-02-15 12:53:10 +02004939
4940 if (!info->attrs[NL80211_ATTR_FRAME] ||
4941 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
4942 return -EINVAL;
4943
Johannes Berg4c476992010-10-04 21:36:35 +02004944 if (!rdev->ops->mgmt_tx)
4945 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004946
Johannes Berg9d38d852010-06-09 17:20:33 +02004947 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004948 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004949 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4950 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4951 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08004952 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02004953 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4954 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004955
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004956 if (info->attrs[NL80211_ATTR_DURATION]) {
4957 if (!rdev->ops->mgmt_tx_cancel_wait)
4958 return -EINVAL;
4959 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4960 }
4961
Jouni Malinen026331c2010-02-15 12:53:10 +02004962 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4963 channel_type = nla_get_u32(
4964 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4965 if (channel_type != NL80211_CHAN_NO_HT &&
4966 channel_type != NL80211_CHAN_HT20 &&
4967 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02004968 channel_type != NL80211_CHAN_HT40MINUS)
4969 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02004970 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02004971 }
4972
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004973 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
4974
Jouni Malinen026331c2010-02-15 12:53:10 +02004975 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4976 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02004977 if (chan == NULL)
4978 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02004979
4980 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004981 if (!msg)
4982 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02004983
4984 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg2e161f72010-08-12 15:38:38 +02004985 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02004986
4987 if (IS_ERR(hdr)) {
4988 err = PTR_ERR(hdr);
4989 goto free_msg;
4990 }
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004991 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
4992 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02004993 nla_data(info->attrs[NL80211_ATTR_FRAME]),
4994 nla_len(info->attrs[NL80211_ATTR_FRAME]),
4995 &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02004996 if (err)
4997 goto free_msg;
4998
4999 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5000
5001 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005002 return genlmsg_reply(msg, info);
Jouni Malinen026331c2010-02-15 12:53:10 +02005003
5004 nla_put_failure:
5005 err = -ENOBUFS;
5006 free_msg:
5007 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005008 return err;
5009}
5010
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005011static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5012{
5013 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5014 struct net_device *dev = info->user_ptr[1];
5015 u64 cookie;
5016
5017 if (!info->attrs[NL80211_ATTR_COOKIE])
5018 return -EINVAL;
5019
5020 if (!rdev->ops->mgmt_tx_cancel_wait)
5021 return -EOPNOTSUPP;
5022
5023 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5024 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5025 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5026 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5027 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5028 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5029 return -EOPNOTSUPP;
5030
5031 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5032
5033 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5034}
5035
Kalle Valoffb9eb32010-02-17 17:58:10 +02005036static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5037{
Johannes Berg4c476992010-10-04 21:36:35 +02005038 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005039 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005040 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005041 u8 ps_state;
5042 bool state;
5043 int err;
5044
Johannes Berg4c476992010-10-04 21:36:35 +02005045 if (!info->attrs[NL80211_ATTR_PS_STATE])
5046 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005047
5048 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5049
Johannes Berg4c476992010-10-04 21:36:35 +02005050 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5051 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005052
5053 wdev = dev->ieee80211_ptr;
5054
Johannes Berg4c476992010-10-04 21:36:35 +02005055 if (!rdev->ops->set_power_mgmt)
5056 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005057
5058 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5059
5060 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005061 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005062
Johannes Berg4c476992010-10-04 21:36:35 +02005063 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5064 wdev->ps_timeout);
5065 if (!err)
5066 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005067 return err;
5068}
5069
5070static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5071{
Johannes Berg4c476992010-10-04 21:36:35 +02005072 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005073 enum nl80211_ps_state ps_state;
5074 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005075 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005076 struct sk_buff *msg;
5077 void *hdr;
5078 int err;
5079
Kalle Valoffb9eb32010-02-17 17:58:10 +02005080 wdev = dev->ieee80211_ptr;
5081
Johannes Berg4c476992010-10-04 21:36:35 +02005082 if (!rdev->ops->set_power_mgmt)
5083 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005084
5085 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005086 if (!msg)
5087 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005088
5089 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5090 NL80211_CMD_GET_POWER_SAVE);
5091 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005092 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005093 goto free_msg;
5094 }
5095
5096 if (wdev->ps)
5097 ps_state = NL80211_PS_ENABLED;
5098 else
5099 ps_state = NL80211_PS_DISABLED;
5100
5101 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5102
5103 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005104 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005105
Johannes Berg4c476992010-10-04 21:36:35 +02005106 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005107 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005108 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005109 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005110 return err;
5111}
5112
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005113static struct nla_policy
5114nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5115 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5116 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5117 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5118};
5119
5120static int nl80211_set_cqm_rssi(struct genl_info *info,
5121 s32 threshold, u32 hysteresis)
5122{
Johannes Berg4c476992010-10-04 21:36:35 +02005123 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005124 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005125 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005126
5127 if (threshold > 0)
5128 return -EINVAL;
5129
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005130 wdev = dev->ieee80211_ptr;
5131
Johannes Berg4c476992010-10-04 21:36:35 +02005132 if (!rdev->ops->set_cqm_rssi_config)
5133 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005134
Johannes Berg074ac8d2010-09-16 14:58:22 +02005135 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005136 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
5137 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005138
Johannes Berg4c476992010-10-04 21:36:35 +02005139 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5140 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005141}
5142
5143static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5144{
5145 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5146 struct nlattr *cqm;
5147 int err;
5148
5149 cqm = info->attrs[NL80211_ATTR_CQM];
5150 if (!cqm) {
5151 err = -EINVAL;
5152 goto out;
5153 }
5154
5155 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5156 nl80211_attr_cqm_policy);
5157 if (err)
5158 goto out;
5159
5160 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5161 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5162 s32 threshold;
5163 u32 hysteresis;
5164 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5165 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5166 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5167 } else
5168 err = -EINVAL;
5169
5170out:
5171 return err;
5172}
5173
Johannes Berg29cbe682010-12-03 09:20:44 +01005174static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
5175{
5176 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5177 struct net_device *dev = info->user_ptr[1];
5178 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08005179 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01005180 int err;
5181
5182 /* start with default */
5183 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08005184 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01005185
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005186 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01005187 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005188 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01005189 if (err)
5190 return err;
5191 }
5192
5193 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
5194 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
5195 return -EINVAL;
5196
Javier Cardonac80d5452010-12-16 17:37:49 -08005197 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
5198 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
5199
5200 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
5201 /* parse additional setup parameters if given */
5202 err = nl80211_parse_mesh_setup(info, &setup);
5203 if (err)
5204 return err;
5205 }
5206
5207 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01005208}
5209
5210static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
5211{
5212 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5213 struct net_device *dev = info->user_ptr[1];
5214
5215 return cfg80211_leave_mesh(rdev, dev);
5216}
5217
Johannes Bergff1b6e62011-05-04 15:37:28 +02005218static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
5219{
5220 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5221 struct sk_buff *msg;
5222 void *hdr;
5223
5224 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5225 return -EOPNOTSUPP;
5226
5227 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5228 if (!msg)
5229 return -ENOMEM;
5230
5231 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5232 NL80211_CMD_GET_WOWLAN);
5233 if (!hdr)
5234 goto nla_put_failure;
5235
5236 if (rdev->wowlan) {
5237 struct nlattr *nl_wowlan;
5238
5239 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
5240 if (!nl_wowlan)
5241 goto nla_put_failure;
5242
5243 if (rdev->wowlan->any)
5244 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
5245 if (rdev->wowlan->disconnect)
5246 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
5247 if (rdev->wowlan->magic_pkt)
5248 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
5249 if (rdev->wowlan->n_patterns) {
5250 struct nlattr *nl_pats, *nl_pat;
5251 int i, pat_len;
5252
5253 nl_pats = nla_nest_start(msg,
5254 NL80211_WOWLAN_TRIG_PKT_PATTERN);
5255 if (!nl_pats)
5256 goto nla_put_failure;
5257
5258 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
5259 nl_pat = nla_nest_start(msg, i + 1);
5260 if (!nl_pat)
5261 goto nla_put_failure;
5262 pat_len = rdev->wowlan->patterns[i].pattern_len;
5263 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
5264 DIV_ROUND_UP(pat_len, 8),
5265 rdev->wowlan->patterns[i].mask);
5266 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
5267 pat_len,
5268 rdev->wowlan->patterns[i].pattern);
5269 nla_nest_end(msg, nl_pat);
5270 }
5271 nla_nest_end(msg, nl_pats);
5272 }
5273
5274 nla_nest_end(msg, nl_wowlan);
5275 }
5276
5277 genlmsg_end(msg, hdr);
5278 return genlmsg_reply(msg, info);
5279
5280nla_put_failure:
5281 nlmsg_free(msg);
5282 return -ENOBUFS;
5283}
5284
5285static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
5286{
5287 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5288 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
5289 struct cfg80211_wowlan no_triggers = {};
5290 struct cfg80211_wowlan new_triggers = {};
5291 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
5292 int err, i;
5293
5294 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5295 return -EOPNOTSUPP;
5296
5297 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
5298 goto no_triggers;
5299
5300 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
5301 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5302 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5303 nl80211_wowlan_policy);
5304 if (err)
5305 return err;
5306
5307 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
5308 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
5309 return -EINVAL;
5310 new_triggers.any = true;
5311 }
5312
5313 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
5314 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
5315 return -EINVAL;
5316 new_triggers.disconnect = true;
5317 }
5318
5319 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
5320 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
5321 return -EINVAL;
5322 new_triggers.magic_pkt = true;
5323 }
5324
5325 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
5326 struct nlattr *pat;
5327 int n_patterns = 0;
5328 int rem, pat_len, mask_len;
5329 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
5330
5331 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5332 rem)
5333 n_patterns++;
5334 if (n_patterns > wowlan->n_patterns)
5335 return -EINVAL;
5336
5337 new_triggers.patterns = kcalloc(n_patterns,
5338 sizeof(new_triggers.patterns[0]),
5339 GFP_KERNEL);
5340 if (!new_triggers.patterns)
5341 return -ENOMEM;
5342
5343 new_triggers.n_patterns = n_patterns;
5344 i = 0;
5345
5346 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5347 rem) {
5348 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
5349 nla_data(pat), nla_len(pat), NULL);
5350 err = -EINVAL;
5351 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
5352 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
5353 goto error;
5354 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
5355 mask_len = DIV_ROUND_UP(pat_len, 8);
5356 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
5357 mask_len)
5358 goto error;
5359 if (pat_len > wowlan->pattern_max_len ||
5360 pat_len < wowlan->pattern_min_len)
5361 goto error;
5362
5363 new_triggers.patterns[i].mask =
5364 kmalloc(mask_len + pat_len, GFP_KERNEL);
5365 if (!new_triggers.patterns[i].mask) {
5366 err = -ENOMEM;
5367 goto error;
5368 }
5369 new_triggers.patterns[i].pattern =
5370 new_triggers.patterns[i].mask + mask_len;
5371 memcpy(new_triggers.patterns[i].mask,
5372 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
5373 mask_len);
5374 new_triggers.patterns[i].pattern_len = pat_len;
5375 memcpy(new_triggers.patterns[i].pattern,
5376 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
5377 pat_len);
5378 i++;
5379 }
5380 }
5381
5382 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
5383 struct cfg80211_wowlan *ntrig;
5384 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
5385 GFP_KERNEL);
5386 if (!ntrig) {
5387 err = -ENOMEM;
5388 goto error;
5389 }
5390 cfg80211_rdev_free_wowlan(rdev);
5391 rdev->wowlan = ntrig;
5392 } else {
5393 no_triggers:
5394 cfg80211_rdev_free_wowlan(rdev);
5395 rdev->wowlan = NULL;
5396 }
5397
5398 return 0;
5399 error:
5400 for (i = 0; i < new_triggers.n_patterns; i++)
5401 kfree(new_triggers.patterns[i].mask);
5402 kfree(new_triggers.patterns);
5403 return err;
5404}
5405
Johannes Berg4c476992010-10-04 21:36:35 +02005406#define NL80211_FLAG_NEED_WIPHY 0x01
5407#define NL80211_FLAG_NEED_NETDEV 0x02
5408#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02005409#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
5410#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
5411 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02005412
5413static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
5414 struct genl_info *info)
5415{
5416 struct cfg80211_registered_device *rdev;
5417 struct net_device *dev;
5418 int err;
5419 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
5420
5421 if (rtnl)
5422 rtnl_lock();
5423
5424 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
5425 rdev = cfg80211_get_dev_from_info(info);
5426 if (IS_ERR(rdev)) {
5427 if (rtnl)
5428 rtnl_unlock();
5429 return PTR_ERR(rdev);
5430 }
5431 info->user_ptr[0] = rdev;
5432 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
5433 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5434 if (err) {
5435 if (rtnl)
5436 rtnl_unlock();
5437 return err;
5438 }
Johannes Berg41265712010-10-04 21:14:05 +02005439 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
5440 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02005441 cfg80211_unlock_rdev(rdev);
5442 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02005443 if (rtnl)
5444 rtnl_unlock();
5445 return -ENETDOWN;
5446 }
Johannes Berg4c476992010-10-04 21:36:35 +02005447 info->user_ptr[0] = rdev;
5448 info->user_ptr[1] = dev;
5449 }
5450
5451 return 0;
5452}
5453
5454static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
5455 struct genl_info *info)
5456{
5457 if (info->user_ptr[0])
5458 cfg80211_unlock_rdev(info->user_ptr[0]);
5459 if (info->user_ptr[1])
5460 dev_put(info->user_ptr[1]);
5461 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
5462 rtnl_unlock();
5463}
5464
Johannes Berg55682962007-09-20 13:09:35 -04005465static struct genl_ops nl80211_ops[] = {
5466 {
5467 .cmd = NL80211_CMD_GET_WIPHY,
5468 .doit = nl80211_get_wiphy,
5469 .dumpit = nl80211_dump_wiphy,
5470 .policy = nl80211_policy,
5471 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005472 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04005473 },
5474 {
5475 .cmd = NL80211_CMD_SET_WIPHY,
5476 .doit = nl80211_set_wiphy,
5477 .policy = nl80211_policy,
5478 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005479 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005480 },
5481 {
5482 .cmd = NL80211_CMD_GET_INTERFACE,
5483 .doit = nl80211_get_interface,
5484 .dumpit = nl80211_dump_interface,
5485 .policy = nl80211_policy,
5486 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005487 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04005488 },
5489 {
5490 .cmd = NL80211_CMD_SET_INTERFACE,
5491 .doit = nl80211_set_interface,
5492 .policy = nl80211_policy,
5493 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005494 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5495 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005496 },
5497 {
5498 .cmd = NL80211_CMD_NEW_INTERFACE,
5499 .doit = nl80211_new_interface,
5500 .policy = nl80211_policy,
5501 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005502 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5503 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005504 },
5505 {
5506 .cmd = NL80211_CMD_DEL_INTERFACE,
5507 .doit = nl80211_del_interface,
5508 .policy = nl80211_policy,
5509 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005510 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5511 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005512 },
Johannes Berg41ade002007-12-19 02:03:29 +01005513 {
5514 .cmd = NL80211_CMD_GET_KEY,
5515 .doit = nl80211_get_key,
5516 .policy = nl80211_policy,
5517 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005518 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5519 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005520 },
5521 {
5522 .cmd = NL80211_CMD_SET_KEY,
5523 .doit = nl80211_set_key,
5524 .policy = nl80211_policy,
5525 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005526 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005527 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005528 },
5529 {
5530 .cmd = NL80211_CMD_NEW_KEY,
5531 .doit = nl80211_new_key,
5532 .policy = nl80211_policy,
5533 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005534 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005535 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005536 },
5537 {
5538 .cmd = NL80211_CMD_DEL_KEY,
5539 .doit = nl80211_del_key,
5540 .policy = nl80211_policy,
5541 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005542 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005543 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005544 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01005545 {
5546 .cmd = NL80211_CMD_SET_BEACON,
5547 .policy = nl80211_policy,
5548 .flags = GENL_ADMIN_PERM,
5549 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005550 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5551 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005552 },
5553 {
5554 .cmd = NL80211_CMD_NEW_BEACON,
5555 .policy = nl80211_policy,
5556 .flags = GENL_ADMIN_PERM,
5557 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005558 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5559 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005560 },
5561 {
5562 .cmd = NL80211_CMD_DEL_BEACON,
5563 .policy = nl80211_policy,
5564 .flags = GENL_ADMIN_PERM,
5565 .doit = nl80211_del_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005566 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5567 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005568 },
Johannes Berg5727ef12007-12-19 02:03:34 +01005569 {
5570 .cmd = NL80211_CMD_GET_STATION,
5571 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005572 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01005573 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02005574 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5575 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005576 },
5577 {
5578 .cmd = NL80211_CMD_SET_STATION,
5579 .doit = nl80211_set_station,
5580 .policy = nl80211_policy,
5581 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005582 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5583 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005584 },
5585 {
5586 .cmd = NL80211_CMD_NEW_STATION,
5587 .doit = nl80211_new_station,
5588 .policy = nl80211_policy,
5589 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005590 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005591 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005592 },
5593 {
5594 .cmd = NL80211_CMD_DEL_STATION,
5595 .doit = nl80211_del_station,
5596 .policy = nl80211_policy,
5597 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005598 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5599 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005600 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005601 {
5602 .cmd = NL80211_CMD_GET_MPATH,
5603 .doit = nl80211_get_mpath,
5604 .dumpit = nl80211_dump_mpath,
5605 .policy = nl80211_policy,
5606 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005607 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005608 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005609 },
5610 {
5611 .cmd = NL80211_CMD_SET_MPATH,
5612 .doit = nl80211_set_mpath,
5613 .policy = nl80211_policy,
5614 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005615 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005616 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005617 },
5618 {
5619 .cmd = NL80211_CMD_NEW_MPATH,
5620 .doit = nl80211_new_mpath,
5621 .policy = nl80211_policy,
5622 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005623 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005624 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005625 },
5626 {
5627 .cmd = NL80211_CMD_DEL_MPATH,
5628 .doit = nl80211_del_mpath,
5629 .policy = nl80211_policy,
5630 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005631 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5632 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005633 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005634 {
5635 .cmd = NL80211_CMD_SET_BSS,
5636 .doit = nl80211_set_bss,
5637 .policy = nl80211_policy,
5638 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005639 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5640 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005641 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005642 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08005643 .cmd = NL80211_CMD_GET_REG,
5644 .doit = nl80211_get_reg,
5645 .policy = nl80211_policy,
5646 /* can be retrieved by unprivileged users */
5647 },
5648 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005649 .cmd = NL80211_CMD_SET_REG,
5650 .doit = nl80211_set_reg,
5651 .policy = nl80211_policy,
5652 .flags = GENL_ADMIN_PERM,
5653 },
5654 {
5655 .cmd = NL80211_CMD_REQ_SET_REG,
5656 .doit = nl80211_req_set_reg,
5657 .policy = nl80211_policy,
5658 .flags = GENL_ADMIN_PERM,
5659 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005660 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005661 .cmd = NL80211_CMD_GET_MESH_CONFIG,
5662 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005663 .policy = nl80211_policy,
5664 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005665 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5666 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005667 },
5668 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005669 .cmd = NL80211_CMD_SET_MESH_CONFIG,
5670 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005671 .policy = nl80211_policy,
5672 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01005673 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005674 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005675 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02005676 {
Johannes Berg2a519312009-02-10 21:25:55 +01005677 .cmd = NL80211_CMD_TRIGGER_SCAN,
5678 .doit = nl80211_trigger_scan,
5679 .policy = nl80211_policy,
5680 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005681 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005682 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01005683 },
5684 {
5685 .cmd = NL80211_CMD_GET_SCAN,
5686 .policy = nl80211_policy,
5687 .dumpit = nl80211_dump_scan,
5688 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02005689 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03005690 .cmd = NL80211_CMD_START_SCHED_SCAN,
5691 .doit = nl80211_start_sched_scan,
5692 .policy = nl80211_policy,
5693 .flags = GENL_ADMIN_PERM,
5694 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5695 NL80211_FLAG_NEED_RTNL,
5696 },
5697 {
5698 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
5699 .doit = nl80211_stop_sched_scan,
5700 .policy = nl80211_policy,
5701 .flags = GENL_ADMIN_PERM,
5702 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5703 NL80211_FLAG_NEED_RTNL,
5704 },
5705 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02005706 .cmd = NL80211_CMD_AUTHENTICATE,
5707 .doit = nl80211_authenticate,
5708 .policy = nl80211_policy,
5709 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005710 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005711 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005712 },
5713 {
5714 .cmd = NL80211_CMD_ASSOCIATE,
5715 .doit = nl80211_associate,
5716 .policy = nl80211_policy,
5717 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005718 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005719 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005720 },
5721 {
5722 .cmd = NL80211_CMD_DEAUTHENTICATE,
5723 .doit = nl80211_deauthenticate,
5724 .policy = nl80211_policy,
5725 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005726 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005727 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005728 },
5729 {
5730 .cmd = NL80211_CMD_DISASSOCIATE,
5731 .doit = nl80211_disassociate,
5732 .policy = nl80211_policy,
5733 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005734 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005735 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005736 },
Johannes Berg04a773a2009-04-19 21:24:32 +02005737 {
5738 .cmd = NL80211_CMD_JOIN_IBSS,
5739 .doit = nl80211_join_ibss,
5740 .policy = nl80211_policy,
5741 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005742 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005743 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02005744 },
5745 {
5746 .cmd = NL80211_CMD_LEAVE_IBSS,
5747 .doit = nl80211_leave_ibss,
5748 .policy = nl80211_policy,
5749 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005750 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005751 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02005752 },
Johannes Bergaff89a92009-07-01 21:26:51 +02005753#ifdef CONFIG_NL80211_TESTMODE
5754 {
5755 .cmd = NL80211_CMD_TESTMODE,
5756 .doit = nl80211_testmode_do,
5757 .policy = nl80211_policy,
5758 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005759 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5760 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02005761 },
5762#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02005763 {
5764 .cmd = NL80211_CMD_CONNECT,
5765 .doit = nl80211_connect,
5766 .policy = nl80211_policy,
5767 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005768 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005769 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005770 },
5771 {
5772 .cmd = NL80211_CMD_DISCONNECT,
5773 .doit = nl80211_disconnect,
5774 .policy = nl80211_policy,
5775 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005776 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005777 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005778 },
Johannes Berg463d0182009-07-14 00:33:35 +02005779 {
5780 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
5781 .doit = nl80211_wiphy_netns,
5782 .policy = nl80211_policy,
5783 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005784 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5785 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02005786 },
Holger Schurig61fa7132009-11-11 12:25:40 +01005787 {
5788 .cmd = NL80211_CMD_GET_SURVEY,
5789 .policy = nl80211_policy,
5790 .dumpit = nl80211_dump_survey,
5791 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005792 {
5793 .cmd = NL80211_CMD_SET_PMKSA,
5794 .doit = nl80211_setdel_pmksa,
5795 .policy = nl80211_policy,
5796 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005797 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5798 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005799 },
5800 {
5801 .cmd = NL80211_CMD_DEL_PMKSA,
5802 .doit = nl80211_setdel_pmksa,
5803 .policy = nl80211_policy,
5804 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005805 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5806 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005807 },
5808 {
5809 .cmd = NL80211_CMD_FLUSH_PMKSA,
5810 .doit = nl80211_flush_pmksa,
5811 .policy = nl80211_policy,
5812 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005813 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5814 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005815 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005816 {
5817 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
5818 .doit = nl80211_remain_on_channel,
5819 .policy = nl80211_policy,
5820 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005821 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005822 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005823 },
5824 {
5825 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5826 .doit = nl80211_cancel_remain_on_channel,
5827 .policy = nl80211_policy,
5828 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005829 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005830 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005831 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005832 {
5833 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
5834 .doit = nl80211_set_tx_bitrate_mask,
5835 .policy = nl80211_policy,
5836 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005837 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5838 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005839 },
Jouni Malinen026331c2010-02-15 12:53:10 +02005840 {
Johannes Berg2e161f72010-08-12 15:38:38 +02005841 .cmd = NL80211_CMD_REGISTER_FRAME,
5842 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02005843 .policy = nl80211_policy,
5844 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005845 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5846 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02005847 },
5848 {
Johannes Berg2e161f72010-08-12 15:38:38 +02005849 .cmd = NL80211_CMD_FRAME,
5850 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02005851 .policy = nl80211_policy,
5852 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005853 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005854 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02005855 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02005856 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005857 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
5858 .doit = nl80211_tx_mgmt_cancel_wait,
5859 .policy = nl80211_policy,
5860 .flags = GENL_ADMIN_PERM,
5861 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5862 NL80211_FLAG_NEED_RTNL,
5863 },
5864 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02005865 .cmd = NL80211_CMD_SET_POWER_SAVE,
5866 .doit = nl80211_set_power_save,
5867 .policy = nl80211_policy,
5868 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005869 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5870 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02005871 },
5872 {
5873 .cmd = NL80211_CMD_GET_POWER_SAVE,
5874 .doit = nl80211_get_power_save,
5875 .policy = nl80211_policy,
5876 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005877 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5878 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02005879 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005880 {
5881 .cmd = NL80211_CMD_SET_CQM,
5882 .doit = nl80211_set_cqm,
5883 .policy = nl80211_policy,
5884 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005885 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5886 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005887 },
Johannes Bergf444de02010-05-05 15:25:02 +02005888 {
5889 .cmd = NL80211_CMD_SET_CHANNEL,
5890 .doit = nl80211_set_channel,
5891 .policy = nl80211_policy,
5892 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005893 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5894 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02005895 },
Bill Jordane8347eb2010-10-01 13:54:28 -04005896 {
5897 .cmd = NL80211_CMD_SET_WDS_PEER,
5898 .doit = nl80211_set_wds_peer,
5899 .policy = nl80211_policy,
5900 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02005901 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5902 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04005903 },
Johannes Berg29cbe682010-12-03 09:20:44 +01005904 {
5905 .cmd = NL80211_CMD_JOIN_MESH,
5906 .doit = nl80211_join_mesh,
5907 .policy = nl80211_policy,
5908 .flags = GENL_ADMIN_PERM,
5909 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5910 NL80211_FLAG_NEED_RTNL,
5911 },
5912 {
5913 .cmd = NL80211_CMD_LEAVE_MESH,
5914 .doit = nl80211_leave_mesh,
5915 .policy = nl80211_policy,
5916 .flags = GENL_ADMIN_PERM,
5917 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5918 NL80211_FLAG_NEED_RTNL,
5919 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02005920 {
5921 .cmd = NL80211_CMD_GET_WOWLAN,
5922 .doit = nl80211_get_wowlan,
5923 .policy = nl80211_policy,
5924 /* can be retrieved by unprivileged users */
5925 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5926 NL80211_FLAG_NEED_RTNL,
5927 },
5928 {
5929 .cmd = NL80211_CMD_SET_WOWLAN,
5930 .doit = nl80211_set_wowlan,
5931 .policy = nl80211_policy,
5932 .flags = GENL_ADMIN_PERM,
5933 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5934 NL80211_FLAG_NEED_RTNL,
5935 },
Johannes Berg55682962007-09-20 13:09:35 -04005936};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005937
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005938static struct genl_multicast_group nl80211_mlme_mcgrp = {
5939 .name = "mlme",
5940};
Johannes Berg55682962007-09-20 13:09:35 -04005941
5942/* multicast groups */
5943static struct genl_multicast_group nl80211_config_mcgrp = {
5944 .name = "config",
5945};
Johannes Berg2a519312009-02-10 21:25:55 +01005946static struct genl_multicast_group nl80211_scan_mcgrp = {
5947 .name = "scan",
5948};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005949static struct genl_multicast_group nl80211_regulatory_mcgrp = {
5950 .name = "regulatory",
5951};
Johannes Berg55682962007-09-20 13:09:35 -04005952
5953/* notification functions */
5954
5955void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
5956{
5957 struct sk_buff *msg;
5958
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005959 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005960 if (!msg)
5961 return;
5962
5963 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
5964 nlmsg_free(msg);
5965 return;
5966 }
5967
Johannes Berg463d0182009-07-14 00:33:35 +02005968 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5969 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005970}
5971
Johannes Berg362a4152009-05-24 16:43:15 +02005972static int nl80211_add_scan_req(struct sk_buff *msg,
5973 struct cfg80211_registered_device *rdev)
5974{
5975 struct cfg80211_scan_request *req = rdev->scan_req;
5976 struct nlattr *nest;
5977 int i;
5978
Johannes Berg667503d2009-07-07 03:56:11 +02005979 ASSERT_RDEV_LOCK(rdev);
5980
Johannes Berg362a4152009-05-24 16:43:15 +02005981 if (WARN_ON(!req))
5982 return 0;
5983
5984 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
5985 if (!nest)
5986 goto nla_put_failure;
5987 for (i = 0; i < req->n_ssids; i++)
5988 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
5989 nla_nest_end(msg, nest);
5990
5991 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
5992 if (!nest)
5993 goto nla_put_failure;
5994 for (i = 0; i < req->n_channels; i++)
5995 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
5996 nla_nest_end(msg, nest);
5997
5998 if (req->ie)
5999 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
6000
6001 return 0;
6002 nla_put_failure:
6003 return -ENOBUFS;
6004}
6005
Johannes Berga538e2d2009-06-16 19:56:42 +02006006static int nl80211_send_scan_msg(struct sk_buff *msg,
6007 struct cfg80211_registered_device *rdev,
6008 struct net_device *netdev,
6009 u32 pid, u32 seq, int flags,
6010 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01006011{
6012 void *hdr;
6013
6014 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6015 if (!hdr)
6016 return -1;
6017
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05006018 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01006019 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6020
Johannes Berg362a4152009-05-24 16:43:15 +02006021 /* ignore errors and send incomplete event anyway */
6022 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01006023
6024 return genlmsg_end(msg, hdr);
6025
6026 nla_put_failure:
6027 genlmsg_cancel(msg, hdr);
6028 return -EMSGSIZE;
6029}
6030
Luciano Coelho807f8a82011-05-11 17:09:35 +03006031static int
6032nl80211_send_sched_scan_msg(struct sk_buff *msg,
6033 struct cfg80211_registered_device *rdev,
6034 struct net_device *netdev,
6035 u32 pid, u32 seq, int flags, u32 cmd)
6036{
6037 void *hdr;
6038
6039 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6040 if (!hdr)
6041 return -1;
6042
6043 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6044 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6045
6046 return genlmsg_end(msg, hdr);
6047
6048 nla_put_failure:
6049 genlmsg_cancel(msg, hdr);
6050 return -EMSGSIZE;
6051}
6052
Johannes Berga538e2d2009-06-16 19:56:42 +02006053void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
6054 struct net_device *netdev)
6055{
6056 struct sk_buff *msg;
6057
6058 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6059 if (!msg)
6060 return;
6061
6062 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6063 NL80211_CMD_TRIGGER_SCAN) < 0) {
6064 nlmsg_free(msg);
6065 return;
6066 }
6067
Johannes Berg463d0182009-07-14 00:33:35 +02006068 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6069 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02006070}
6071
Johannes Berg2a519312009-02-10 21:25:55 +01006072void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
6073 struct net_device *netdev)
6074{
6075 struct sk_buff *msg;
6076
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006077 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006078 if (!msg)
6079 return;
6080
Johannes Berga538e2d2009-06-16 19:56:42 +02006081 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6082 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006083 nlmsg_free(msg);
6084 return;
6085 }
6086
Johannes Berg463d0182009-07-14 00:33:35 +02006087 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6088 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006089}
6090
6091void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
6092 struct net_device *netdev)
6093{
6094 struct sk_buff *msg;
6095
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006096 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006097 if (!msg)
6098 return;
6099
Johannes Berga538e2d2009-06-16 19:56:42 +02006100 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6101 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006102 nlmsg_free(msg);
6103 return;
6104 }
6105
Johannes Berg463d0182009-07-14 00:33:35 +02006106 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6107 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006108}
6109
Luciano Coelho807f8a82011-05-11 17:09:35 +03006110void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
6111 struct net_device *netdev)
6112{
6113 struct sk_buff *msg;
6114
6115 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6116 if (!msg)
6117 return;
6118
6119 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
6120 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
6121 nlmsg_free(msg);
6122 return;
6123 }
6124
6125 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6126 nl80211_scan_mcgrp.id, GFP_KERNEL);
6127}
6128
6129void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
6130 struct net_device *netdev, u32 cmd)
6131{
6132 struct sk_buff *msg;
6133
6134 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6135 if (!msg)
6136 return;
6137
6138 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
6139 nlmsg_free(msg);
6140 return;
6141 }
6142
6143 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6144 nl80211_scan_mcgrp.id, GFP_KERNEL);
6145}
6146
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006147/*
6148 * This can happen on global regulatory changes or device specific settings
6149 * based on custom world regulatory domains.
6150 */
6151void nl80211_send_reg_change_event(struct regulatory_request *request)
6152{
6153 struct sk_buff *msg;
6154 void *hdr;
6155
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006156 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006157 if (!msg)
6158 return;
6159
6160 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
6161 if (!hdr) {
6162 nlmsg_free(msg);
6163 return;
6164 }
6165
6166 /* Userspace can always count this one always being set */
6167 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
6168
6169 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
6170 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6171 NL80211_REGDOM_TYPE_WORLD);
6172 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
6173 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6174 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
6175 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
6176 request->intersect)
6177 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6178 NL80211_REGDOM_TYPE_INTERSECTION);
6179 else {
6180 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6181 NL80211_REGDOM_TYPE_COUNTRY);
6182 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
6183 }
6184
6185 if (wiphy_idx_valid(request->wiphy_idx))
6186 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
6187
6188 if (genlmsg_end(msg, hdr) < 0) {
6189 nlmsg_free(msg);
6190 return;
6191 }
6192
Johannes Bergbc43b282009-07-25 10:54:13 +02006193 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02006194 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02006195 GFP_ATOMIC);
6196 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006197
6198 return;
6199
6200nla_put_failure:
6201 genlmsg_cancel(msg, hdr);
6202 nlmsg_free(msg);
6203}
6204
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006205static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
6206 struct net_device *netdev,
6207 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006208 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006209{
6210 struct sk_buff *msg;
6211 void *hdr;
6212
Johannes Berge6d6e342009-07-01 21:26:47 +02006213 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006214 if (!msg)
6215 return;
6216
6217 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6218 if (!hdr) {
6219 nlmsg_free(msg);
6220 return;
6221 }
6222
6223 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6224 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6225 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6226
6227 if (genlmsg_end(msg, hdr) < 0) {
6228 nlmsg_free(msg);
6229 return;
6230 }
6231
Johannes Berg463d0182009-07-14 00:33:35 +02006232 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6233 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006234 return;
6235
6236 nla_put_failure:
6237 genlmsg_cancel(msg, hdr);
6238 nlmsg_free(msg);
6239}
6240
6241void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006242 struct net_device *netdev, const u8 *buf,
6243 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006244{
6245 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006246 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006247}
6248
6249void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
6250 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006251 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006252{
Johannes Berge6d6e342009-07-01 21:26:47 +02006253 nl80211_send_mlme_event(rdev, netdev, buf, len,
6254 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006255}
6256
Jouni Malinen53b46b82009-03-27 20:53:56 +02006257void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006258 struct net_device *netdev, const u8 *buf,
6259 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006260{
6261 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006262 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006263}
6264
Jouni Malinen53b46b82009-03-27 20:53:56 +02006265void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
6266 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006267 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006268{
6269 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006270 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006271}
6272
Jouni Malinencf4e5942010-12-16 00:52:40 +02006273void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
6274 struct net_device *netdev, const u8 *buf,
6275 size_t len, gfp_t gfp)
6276{
6277 nl80211_send_mlme_event(rdev, netdev, buf, len,
6278 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
6279}
6280
6281void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
6282 struct net_device *netdev, const u8 *buf,
6283 size_t len, gfp_t gfp)
6284{
6285 nl80211_send_mlme_event(rdev, netdev, buf, len,
6286 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
6287}
6288
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04006289static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
6290 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02006291 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006292{
6293 struct sk_buff *msg;
6294 void *hdr;
6295
Johannes Berge6d6e342009-07-01 21:26:47 +02006296 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006297 if (!msg)
6298 return;
6299
6300 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6301 if (!hdr) {
6302 nlmsg_free(msg);
6303 return;
6304 }
6305
6306 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6307 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6308 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
6309 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6310
6311 if (genlmsg_end(msg, hdr) < 0) {
6312 nlmsg_free(msg);
6313 return;
6314 }
6315
Johannes Berg463d0182009-07-14 00:33:35 +02006316 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6317 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006318 return;
6319
6320 nla_put_failure:
6321 genlmsg_cancel(msg, hdr);
6322 nlmsg_free(msg);
6323}
6324
6325void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006326 struct net_device *netdev, const u8 *addr,
6327 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006328{
6329 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02006330 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006331}
6332
6333void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006334 struct net_device *netdev, const u8 *addr,
6335 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006336{
Johannes Berge6d6e342009-07-01 21:26:47 +02006337 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
6338 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006339}
6340
Samuel Ortizb23aa672009-07-01 21:26:54 +02006341void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
6342 struct net_device *netdev, const u8 *bssid,
6343 const u8 *req_ie, size_t req_ie_len,
6344 const u8 *resp_ie, size_t resp_ie_len,
6345 u16 status, gfp_t gfp)
6346{
6347 struct sk_buff *msg;
6348 void *hdr;
6349
6350 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6351 if (!msg)
6352 return;
6353
6354 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
6355 if (!hdr) {
6356 nlmsg_free(msg);
6357 return;
6358 }
6359
6360 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6361 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6362 if (bssid)
6363 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6364 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
6365 if (req_ie)
6366 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6367 if (resp_ie)
6368 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6369
6370 if (genlmsg_end(msg, hdr) < 0) {
6371 nlmsg_free(msg);
6372 return;
6373 }
6374
Johannes Berg463d0182009-07-14 00:33:35 +02006375 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6376 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006377 return;
6378
6379 nla_put_failure:
6380 genlmsg_cancel(msg, hdr);
6381 nlmsg_free(msg);
6382
6383}
6384
6385void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
6386 struct net_device *netdev, const u8 *bssid,
6387 const u8 *req_ie, size_t req_ie_len,
6388 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
6389{
6390 struct sk_buff *msg;
6391 void *hdr;
6392
6393 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6394 if (!msg)
6395 return;
6396
6397 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
6398 if (!hdr) {
6399 nlmsg_free(msg);
6400 return;
6401 }
6402
6403 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6404 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6405 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6406 if (req_ie)
6407 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6408 if (resp_ie)
6409 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6410
6411 if (genlmsg_end(msg, hdr) < 0) {
6412 nlmsg_free(msg);
6413 return;
6414 }
6415
Johannes Berg463d0182009-07-14 00:33:35 +02006416 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6417 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006418 return;
6419
6420 nla_put_failure:
6421 genlmsg_cancel(msg, hdr);
6422 nlmsg_free(msg);
6423
6424}
6425
6426void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
6427 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02006428 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02006429{
6430 struct sk_buff *msg;
6431 void *hdr;
6432
Johannes Berg667503d2009-07-07 03:56:11 +02006433 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006434 if (!msg)
6435 return;
6436
6437 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
6438 if (!hdr) {
6439 nlmsg_free(msg);
6440 return;
6441 }
6442
6443 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6444 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6445 if (from_ap && reason)
6446 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
6447 if (from_ap)
6448 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
6449 if (ie)
6450 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
6451
6452 if (genlmsg_end(msg, hdr) < 0) {
6453 nlmsg_free(msg);
6454 return;
6455 }
6456
Johannes Berg463d0182009-07-14 00:33:35 +02006457 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6458 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006459 return;
6460
6461 nla_put_failure:
6462 genlmsg_cancel(msg, hdr);
6463 nlmsg_free(msg);
6464
6465}
6466
Johannes Berg04a773a2009-04-19 21:24:32 +02006467void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
6468 struct net_device *netdev, const u8 *bssid,
6469 gfp_t gfp)
6470{
6471 struct sk_buff *msg;
6472 void *hdr;
6473
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006474 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006475 if (!msg)
6476 return;
6477
6478 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
6479 if (!hdr) {
6480 nlmsg_free(msg);
6481 return;
6482 }
6483
6484 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6485 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6486 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6487
6488 if (genlmsg_end(msg, hdr) < 0) {
6489 nlmsg_free(msg);
6490 return;
6491 }
6492
Johannes Berg463d0182009-07-14 00:33:35 +02006493 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6494 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006495 return;
6496
6497 nla_put_failure:
6498 genlmsg_cancel(msg, hdr);
6499 nlmsg_free(msg);
6500}
6501
Javier Cardonac93b5e72011-04-07 15:08:34 -07006502void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
6503 struct net_device *netdev,
6504 const u8 *macaddr, const u8* ie, u8 ie_len,
6505 gfp_t gfp)
6506{
6507 struct sk_buff *msg;
6508 void *hdr;
6509
6510 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6511 if (!msg)
6512 return;
6513
6514 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
6515 if (!hdr) {
6516 nlmsg_free(msg);
6517 return;
6518 }
6519
6520 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6521 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6522 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
6523 if (ie_len && ie)
6524 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
6525
6526 if (genlmsg_end(msg, hdr) < 0) {
6527 nlmsg_free(msg);
6528 return;
6529 }
6530
6531 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6532 nl80211_mlme_mcgrp.id, gfp);
6533 return;
6534
6535 nla_put_failure:
6536 genlmsg_cancel(msg, hdr);
6537 nlmsg_free(msg);
6538}
6539
Jouni Malinena3b8b052009-03-27 21:59:49 +02006540void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
6541 struct net_device *netdev, const u8 *addr,
6542 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02006543 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02006544{
6545 struct sk_buff *msg;
6546 void *hdr;
6547
Johannes Berge6d6e342009-07-01 21:26:47 +02006548 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006549 if (!msg)
6550 return;
6551
6552 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
6553 if (!hdr) {
6554 nlmsg_free(msg);
6555 return;
6556 }
6557
6558 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6559 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6560 if (addr)
6561 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6562 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03006563 if (key_id != -1)
6564 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006565 if (tsc)
6566 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
6567
6568 if (genlmsg_end(msg, hdr) < 0) {
6569 nlmsg_free(msg);
6570 return;
6571 }
6572
Johannes Berg463d0182009-07-14 00:33:35 +02006573 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6574 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006575 return;
6576
6577 nla_put_failure:
6578 genlmsg_cancel(msg, hdr);
6579 nlmsg_free(msg);
6580}
6581
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006582void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
6583 struct ieee80211_channel *channel_before,
6584 struct ieee80211_channel *channel_after)
6585{
6586 struct sk_buff *msg;
6587 void *hdr;
6588 struct nlattr *nl_freq;
6589
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006590 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006591 if (!msg)
6592 return;
6593
6594 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
6595 if (!hdr) {
6596 nlmsg_free(msg);
6597 return;
6598 }
6599
6600 /*
6601 * Since we are applying the beacon hint to a wiphy we know its
6602 * wiphy_idx is valid
6603 */
6604 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
6605
6606 /* Before */
6607 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
6608 if (!nl_freq)
6609 goto nla_put_failure;
6610 if (nl80211_msg_put_channel(msg, channel_before))
6611 goto nla_put_failure;
6612 nla_nest_end(msg, nl_freq);
6613
6614 /* After */
6615 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
6616 if (!nl_freq)
6617 goto nla_put_failure;
6618 if (nl80211_msg_put_channel(msg, channel_after))
6619 goto nla_put_failure;
6620 nla_nest_end(msg, nl_freq);
6621
6622 if (genlmsg_end(msg, hdr) < 0) {
6623 nlmsg_free(msg);
6624 return;
6625 }
6626
Johannes Berg463d0182009-07-14 00:33:35 +02006627 rcu_read_lock();
6628 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
6629 GFP_ATOMIC);
6630 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006631
6632 return;
6633
6634nla_put_failure:
6635 genlmsg_cancel(msg, hdr);
6636 nlmsg_free(msg);
6637}
6638
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006639static void nl80211_send_remain_on_chan_event(
6640 int cmd, struct cfg80211_registered_device *rdev,
6641 struct net_device *netdev, u64 cookie,
6642 struct ieee80211_channel *chan,
6643 enum nl80211_channel_type channel_type,
6644 unsigned int duration, gfp_t gfp)
6645{
6646 struct sk_buff *msg;
6647 void *hdr;
6648
6649 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6650 if (!msg)
6651 return;
6652
6653 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6654 if (!hdr) {
6655 nlmsg_free(msg);
6656 return;
6657 }
6658
6659 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6660 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6661 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
6662 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
6663 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6664
6665 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
6666 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
6667
6668 if (genlmsg_end(msg, hdr) < 0) {
6669 nlmsg_free(msg);
6670 return;
6671 }
6672
6673 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6674 nl80211_mlme_mcgrp.id, gfp);
6675 return;
6676
6677 nla_put_failure:
6678 genlmsg_cancel(msg, hdr);
6679 nlmsg_free(msg);
6680}
6681
6682void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
6683 struct net_device *netdev, u64 cookie,
6684 struct ieee80211_channel *chan,
6685 enum nl80211_channel_type channel_type,
6686 unsigned int duration, gfp_t gfp)
6687{
6688 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
6689 rdev, netdev, cookie, chan,
6690 channel_type, duration, gfp);
6691}
6692
6693void nl80211_send_remain_on_channel_cancel(
6694 struct cfg80211_registered_device *rdev, struct net_device *netdev,
6695 u64 cookie, struct ieee80211_channel *chan,
6696 enum nl80211_channel_type channel_type, gfp_t gfp)
6697{
6698 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6699 rdev, netdev, cookie, chan,
6700 channel_type, 0, gfp);
6701}
6702
Johannes Berg98b62182009-12-23 13:15:44 +01006703void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
6704 struct net_device *dev, const u8 *mac_addr,
6705 struct station_info *sinfo, gfp_t gfp)
6706{
6707 struct sk_buff *msg;
6708
6709 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6710 if (!msg)
6711 return;
6712
6713 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
6714 nlmsg_free(msg);
6715 return;
6716 }
6717
6718 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6719 nl80211_mlme_mcgrp.id, gfp);
6720}
6721
Jouni Malinenec15e682011-03-23 15:29:52 +02006722void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
6723 struct net_device *dev, const u8 *mac_addr,
6724 gfp_t gfp)
6725{
6726 struct sk_buff *msg;
6727 void *hdr;
6728
6729 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6730 if (!msg)
6731 return;
6732
6733 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
6734 if (!hdr) {
6735 nlmsg_free(msg);
6736 return;
6737 }
6738
6739 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
6740 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
6741
6742 if (genlmsg_end(msg, hdr) < 0) {
6743 nlmsg_free(msg);
6744 return;
6745 }
6746
6747 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6748 nl80211_mlme_mcgrp.id, gfp);
6749 return;
6750
6751 nla_put_failure:
6752 genlmsg_cancel(msg, hdr);
6753 nlmsg_free(msg);
6754}
6755
Johannes Berg2e161f72010-08-12 15:38:38 +02006756int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
6757 struct net_device *netdev, u32 nlpid,
6758 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006759{
6760 struct sk_buff *msg;
6761 void *hdr;
6762 int err;
6763
6764 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6765 if (!msg)
6766 return -ENOMEM;
6767
Johannes Berg2e161f72010-08-12 15:38:38 +02006768 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02006769 if (!hdr) {
6770 nlmsg_free(msg);
6771 return -ENOMEM;
6772 }
6773
6774 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6775 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6776 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
6777 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6778
6779 err = genlmsg_end(msg, hdr);
6780 if (err < 0) {
6781 nlmsg_free(msg);
6782 return err;
6783 }
6784
6785 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
6786 if (err < 0)
6787 return err;
6788 return 0;
6789
6790 nla_put_failure:
6791 genlmsg_cancel(msg, hdr);
6792 nlmsg_free(msg);
6793 return -ENOBUFS;
6794}
6795
Johannes Berg2e161f72010-08-12 15:38:38 +02006796void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
6797 struct net_device *netdev, u64 cookie,
6798 const u8 *buf, size_t len, bool ack,
6799 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006800{
6801 struct sk_buff *msg;
6802 void *hdr;
6803
6804 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6805 if (!msg)
6806 return;
6807
Johannes Berg2e161f72010-08-12 15:38:38 +02006808 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02006809 if (!hdr) {
6810 nlmsg_free(msg);
6811 return;
6812 }
6813
6814 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6815 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6816 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6817 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6818 if (ack)
6819 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
6820
6821 if (genlmsg_end(msg, hdr) < 0) {
6822 nlmsg_free(msg);
6823 return;
6824 }
6825
6826 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
6827 return;
6828
6829 nla_put_failure:
6830 genlmsg_cancel(msg, hdr);
6831 nlmsg_free(msg);
6832}
6833
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006834void
6835nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
6836 struct net_device *netdev,
6837 enum nl80211_cqm_rssi_threshold_event rssi_event,
6838 gfp_t gfp)
6839{
6840 struct sk_buff *msg;
6841 struct nlattr *pinfoattr;
6842 void *hdr;
6843
6844 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6845 if (!msg)
6846 return;
6847
6848 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
6849 if (!hdr) {
6850 nlmsg_free(msg);
6851 return;
6852 }
6853
6854 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6855 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6856
6857 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
6858 if (!pinfoattr)
6859 goto nla_put_failure;
6860
6861 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
6862 rssi_event);
6863
6864 nla_nest_end(msg, pinfoattr);
6865
6866 if (genlmsg_end(msg, hdr) < 0) {
6867 nlmsg_free(msg);
6868 return;
6869 }
6870
6871 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6872 nl80211_mlme_mcgrp.id, gfp);
6873 return;
6874
6875 nla_put_failure:
6876 genlmsg_cancel(msg, hdr);
6877 nlmsg_free(msg);
6878}
6879
Johannes Bergc063dbf2010-11-24 08:10:05 +01006880void
6881nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
6882 struct net_device *netdev, const u8 *peer,
6883 u32 num_packets, gfp_t gfp)
6884{
6885 struct sk_buff *msg;
6886 struct nlattr *pinfoattr;
6887 void *hdr;
6888
6889 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6890 if (!msg)
6891 return;
6892
6893 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
6894 if (!hdr) {
6895 nlmsg_free(msg);
6896 return;
6897 }
6898
6899 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6900 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6901 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
6902
6903 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
6904 if (!pinfoattr)
6905 goto nla_put_failure;
6906
6907 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
6908
6909 nla_nest_end(msg, pinfoattr);
6910
6911 if (genlmsg_end(msg, hdr) < 0) {
6912 nlmsg_free(msg);
6913 return;
6914 }
6915
6916 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6917 nl80211_mlme_mcgrp.id, gfp);
6918 return;
6919
6920 nla_put_failure:
6921 genlmsg_cancel(msg, hdr);
6922 nlmsg_free(msg);
6923}
6924
Jouni Malinen026331c2010-02-15 12:53:10 +02006925static int nl80211_netlink_notify(struct notifier_block * nb,
6926 unsigned long state,
6927 void *_notify)
6928{
6929 struct netlink_notify *notify = _notify;
6930 struct cfg80211_registered_device *rdev;
6931 struct wireless_dev *wdev;
6932
6933 if (state != NETLINK_URELEASE)
6934 return NOTIFY_DONE;
6935
6936 rcu_read_lock();
6937
6938 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
6939 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02006940 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Jouni Malinen026331c2010-02-15 12:53:10 +02006941
6942 rcu_read_unlock();
6943
6944 return NOTIFY_DONE;
6945}
6946
6947static struct notifier_block nl80211_netlink_notifier = {
6948 .notifier_call = nl80211_netlink_notify,
6949};
6950
Johannes Berg55682962007-09-20 13:09:35 -04006951/* initialisation/exit functions */
6952
6953int nl80211_init(void)
6954{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006955 int err;
Johannes Berg55682962007-09-20 13:09:35 -04006956
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006957 err = genl_register_family_with_ops(&nl80211_fam,
6958 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04006959 if (err)
6960 return err;
6961
Johannes Berg55682962007-09-20 13:09:35 -04006962 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
6963 if (err)
6964 goto err_out;
6965
Johannes Berg2a519312009-02-10 21:25:55 +01006966 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
6967 if (err)
6968 goto err_out;
6969
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006970 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
6971 if (err)
6972 goto err_out;
6973
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006974 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
6975 if (err)
6976 goto err_out;
6977
Johannes Bergaff89a92009-07-01 21:26:51 +02006978#ifdef CONFIG_NL80211_TESTMODE
6979 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
6980 if (err)
6981 goto err_out;
6982#endif
6983
Jouni Malinen026331c2010-02-15 12:53:10 +02006984 err = netlink_register_notifier(&nl80211_netlink_notifier);
6985 if (err)
6986 goto err_out;
6987
Johannes Berg55682962007-09-20 13:09:35 -04006988 return 0;
6989 err_out:
6990 genl_unregister_family(&nl80211_fam);
6991 return err;
6992}
6993
6994void nl80211_exit(void)
6995{
Jouni Malinen026331c2010-02-15 12:53:10 +02006996 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04006997 genl_unregister_family(&nl80211_fam);
6998}