blob: a868e47b0e7820993294e1d79eab54bca164e1df [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 Fietkau8610c292010-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
Samuel Ortizb23aa672009-07-01 21:26:54 +02003950static bool nl80211_valid_cipher_suite(u32 cipher)
3951{
3952 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3953 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3954 cipher == WLAN_CIPHER_SUITE_TKIP ||
3955 cipher == WLAN_CIPHER_SUITE_CCMP ||
Deepthi Gowri5b26a952011-11-29 11:22:42 +05303956 cipher == WLAN_CIPHER_SUITE_AES_CMAC ||
3957 cipher == WLAN_CIPHER_SUITE_SMS4;
Samuel Ortizb23aa672009-07-01 21:26:54 +02003958}
3959
3960
Jouni Malinen636a5d32009-03-19 13:39:22 +02003961static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3962{
Johannes Berg4c476992010-10-04 21:36:35 +02003963 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3964 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003965 struct ieee80211_channel *chan;
3966 const u8 *bssid, *ssid, *ie = NULL;
3967 int err, ssid_len, ie_len = 0;
3968 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003969 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003970 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003971
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003972 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3973 return -EINVAL;
3974
3975 if (!info->attrs[NL80211_ATTR_MAC])
3976 return -EINVAL;
3977
Jouni Malinen17780922009-03-27 20:52:47 +02003978 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
3979 return -EINVAL;
3980
Johannes Berg19957bb2009-07-02 17:20:43 +02003981 if (!info->attrs[NL80211_ATTR_SSID])
3982 return -EINVAL;
3983
3984 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
3985 return -EINVAL;
3986
Johannes Bergfffd0932009-07-08 14:22:54 +02003987 err = nl80211_parse_key(info, &key);
3988 if (err)
3989 return err;
3990
3991 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02003992 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
3993 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02003994 if (!key.p.key || !key.p.key_len)
3995 return -EINVAL;
3996 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
3997 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
3998 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
3999 key.p.key_len != WLAN_KEY_LEN_WEP104))
4000 return -EINVAL;
4001 if (key.idx > 4)
4002 return -EINVAL;
4003 } else {
4004 key.p.key_len = 0;
4005 key.p.key = NULL;
4006 }
4007
Johannes Bergafea0b72010-08-10 09:46:42 +02004008 if (key.idx >= 0) {
4009 int i;
4010 bool ok = false;
4011 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4012 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4013 ok = true;
4014 break;
4015 }
4016 }
Johannes Berg4c476992010-10-04 21:36:35 +02004017 if (!ok)
4018 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004019 }
4020
Johannes Berg4c476992010-10-04 21:36:35 +02004021 if (!rdev->ops->auth)
4022 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004023
Johannes Berg074ac8d2010-09-16 14:58:22 +02004024 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004025 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4026 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004027
Johannes Berg19957bb2009-07-02 17:20:43 +02004028 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004029 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004030 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004031 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4032 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004033
Johannes Berg19957bb2009-07-02 17:20:43 +02004034 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4035 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4036
4037 if (info->attrs[NL80211_ATTR_IE]) {
4038 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4039 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4040 }
4041
4042 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004043 if (!nl80211_valid_auth_type(auth_type))
4044 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004045
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004046 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4047
Johannes Berg4c476992010-10-04 21:36:35 +02004048 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4049 ssid, ssid_len, ie, ie_len,
4050 key.p.key, key.p.key_len, key.idx,
4051 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004052}
4053
Johannes Bergc0692b82010-08-27 14:26:53 +03004054static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4055 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004056 struct cfg80211_crypto_settings *settings,
4057 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004058{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004059 memset(settings, 0, sizeof(*settings));
4060
Samuel Ortizb23aa672009-07-01 21:26:54 +02004061 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4062
Johannes Bergc0692b82010-08-27 14:26:53 +03004063 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4064 u16 proto;
4065 proto = nla_get_u16(
4066 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4067 settings->control_port_ethertype = cpu_to_be16(proto);
4068 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4069 proto != ETH_P_PAE)
4070 return -EINVAL;
4071 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4072 settings->control_port_no_encrypt = true;
4073 } else
4074 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4075
Samuel Ortizb23aa672009-07-01 21:26:54 +02004076 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4077 void *data;
4078 int len, i;
4079
4080 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4081 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4082 settings->n_ciphers_pairwise = len / sizeof(u32);
4083
4084 if (len % sizeof(u32))
4085 return -EINVAL;
4086
Johannes Berg3dc27d22009-07-02 21:36:37 +02004087 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004088 return -EINVAL;
4089
4090 memcpy(settings->ciphers_pairwise, data, len);
4091
4092 for (i = 0; i < settings->n_ciphers_pairwise; i++)
4093 if (!nl80211_valid_cipher_suite(
4094 settings->ciphers_pairwise[i]))
4095 return -EINVAL;
4096 }
4097
4098 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4099 settings->cipher_group =
4100 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
4101 if (!nl80211_valid_cipher_suite(settings->cipher_group))
4102 return -EINVAL;
4103 }
4104
4105 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4106 settings->wpa_versions =
4107 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4108 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4109 return -EINVAL;
4110 }
4111
4112 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4113 void *data;
Jack Cheung7878aa12012-01-21 11:44:52 -08004114 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004115
4116 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4117 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4118 settings->n_akm_suites = len / sizeof(u32);
4119
4120 if (len % sizeof(u32))
4121 return -EINVAL;
4122
Jouni Malinen508ed742011-09-21 16:13:07 +03004123 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4124 return -EINVAL;
4125
Samuel Ortizb23aa672009-07-01 21:26:54 +02004126 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004127 }
4128
4129 return 0;
4130}
4131
Jouni Malinen636a5d32009-03-19 13:39:22 +02004132static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4133{
Johannes Berg4c476992010-10-04 21:36:35 +02004134 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4135 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004136 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004137 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004138 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004139 int err, ssid_len, ie_len = 0;
4140 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004141
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004142 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4143 return -EINVAL;
4144
4145 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004146 !info->attrs[NL80211_ATTR_SSID] ||
4147 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004148 return -EINVAL;
4149
Johannes Berg4c476992010-10-04 21:36:35 +02004150 if (!rdev->ops->assoc)
4151 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004152
Johannes Berg074ac8d2010-09-16 14:58:22 +02004153 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004154 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4155 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004156
Johannes Berg19957bb2009-07-02 17:20:43 +02004157 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004158
Johannes Berg19957bb2009-07-02 17:20:43 +02004159 chan = ieee80211_get_channel(&rdev->wiphy,
4160 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004161 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4162 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004163
Johannes Berg19957bb2009-07-02 17:20:43 +02004164 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4165 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004166
4167 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004168 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4169 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004170 }
4171
Jouni Malinendc6382c2009-05-06 22:09:37 +03004172 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004173 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004174 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004175 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004176 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004177 else if (mfp != NL80211_MFP_NO)
4178 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004179 }
4180
Johannes Berg3e5d7642009-07-07 14:37:26 +02004181 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4182 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4183
Johannes Bergc0692b82010-08-27 14:26:53 +03004184 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004185 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004186 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4187 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02004188 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004189
Jouni Malinen636a5d32009-03-19 13:39:22 +02004190 return err;
4191}
4192
4193static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4194{
Johannes Berg4c476992010-10-04 21:36:35 +02004195 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4196 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004197 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004198 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004199 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004200 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004201
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004202 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4203 return -EINVAL;
4204
4205 if (!info->attrs[NL80211_ATTR_MAC])
4206 return -EINVAL;
4207
4208 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4209 return -EINVAL;
4210
Johannes Berg4c476992010-10-04 21:36:35 +02004211 if (!rdev->ops->deauth)
4212 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004213
Johannes Berg074ac8d2010-09-16 14:58:22 +02004214 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004215 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4216 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004217
Johannes Berg19957bb2009-07-02 17:20:43 +02004218 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004219
Johannes Berg19957bb2009-07-02 17:20:43 +02004220 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4221 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004222 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004223 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004224 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004225
4226 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004227 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4228 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004229 }
4230
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004231 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4232
Johannes Berg4c476992010-10-04 21:36:35 +02004233 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4234 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004235}
4236
4237static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4238{
Johannes Berg4c476992010-10-04 21:36:35 +02004239 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4240 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004241 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004242 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004243 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004244 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004245
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004246 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4247 return -EINVAL;
4248
4249 if (!info->attrs[NL80211_ATTR_MAC])
4250 return -EINVAL;
4251
4252 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4253 return -EINVAL;
4254
Johannes Berg4c476992010-10-04 21:36:35 +02004255 if (!rdev->ops->disassoc)
4256 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004257
Johannes Berg074ac8d2010-09-16 14:58:22 +02004258 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004259 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4260 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004261
Johannes Berg19957bb2009-07-02 17:20:43 +02004262 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004263
Johannes Berg19957bb2009-07-02 17:20:43 +02004264 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4265 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004266 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004267 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004268 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004269
4270 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004271 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4272 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004273 }
4274
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004275 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4276
Johannes Berg4c476992010-10-04 21:36:35 +02004277 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4278 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004279}
4280
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004281static bool
4282nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4283 int mcast_rate[IEEE80211_NUM_BANDS],
4284 int rateval)
4285{
4286 struct wiphy *wiphy = &rdev->wiphy;
4287 bool found = false;
4288 int band, i;
4289
4290 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4291 struct ieee80211_supported_band *sband;
4292
4293 sband = wiphy->bands[band];
4294 if (!sband)
4295 continue;
4296
4297 for (i = 0; i < sband->n_bitrates; i++) {
4298 if (sband->bitrates[i].bitrate == rateval) {
4299 mcast_rate[band] = i + 1;
4300 found = true;
4301 break;
4302 }
4303 }
4304 }
4305
4306 return found;
4307}
4308
Johannes Berg04a773a2009-04-19 21:24:32 +02004309static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4310{
Johannes Berg4c476992010-10-04 21:36:35 +02004311 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4312 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004313 struct cfg80211_ibss_params ibss;
4314 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004315 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004316 int err;
4317
Johannes Berg8e30bc52009-04-22 17:45:38 +02004318 memset(&ibss, 0, sizeof(ibss));
4319
Johannes Berg04a773a2009-04-19 21:24:32 +02004320 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4321 return -EINVAL;
4322
4323 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4324 !info->attrs[NL80211_ATTR_SSID] ||
4325 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4326 return -EINVAL;
4327
Johannes Berg8e30bc52009-04-22 17:45:38 +02004328 ibss.beacon_interval = 100;
4329
4330 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4331 ibss.beacon_interval =
4332 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4333 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4334 return -EINVAL;
4335 }
4336
Johannes Berg4c476992010-10-04 21:36:35 +02004337 if (!rdev->ops->join_ibss)
4338 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004339
Johannes Berg4c476992010-10-04 21:36:35 +02004340 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4341 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004342
Johannes Berg79c97e92009-07-07 03:56:12 +02004343 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004344
4345 if (info->attrs[NL80211_ATTR_MAC])
4346 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4347 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4348 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4349
4350 if (info->attrs[NL80211_ATTR_IE]) {
4351 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4352 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4353 }
4354
4355 ibss.channel = ieee80211_get_channel(wiphy,
4356 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4357 if (!ibss.channel ||
4358 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004359 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4360 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004361
4362 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004363 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004364
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004365 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4366 u8 *rates =
4367 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4368 int n_rates =
4369 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4370 struct ieee80211_supported_band *sband =
4371 wiphy->bands[ibss.channel->band];
4372 int i, j;
4373
Johannes Berg4c476992010-10-04 21:36:35 +02004374 if (n_rates == 0)
4375 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004376
4377 for (i = 0; i < n_rates; i++) {
4378 int rate = (rates[i] & 0x7f) * 5;
4379 bool found = false;
4380
4381 for (j = 0; j < sband->n_bitrates; j++) {
4382 if (sband->bitrates[j].bitrate == rate) {
4383 found = true;
4384 ibss.basic_rates |= BIT(j);
4385 break;
4386 }
4387 }
Johannes Berg4c476992010-10-04 21:36:35 +02004388 if (!found)
4389 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004390 }
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004391 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004392
4393 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4394 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4395 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4396 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004397
Johannes Berg4c476992010-10-04 21:36:35 +02004398 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4399 connkeys = nl80211_parse_connkeys(rdev,
4400 info->attrs[NL80211_ATTR_KEYS]);
4401 if (IS_ERR(connkeys))
4402 return PTR_ERR(connkeys);
4403 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004404
Johannes Berg4c476992010-10-04 21:36:35 +02004405 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004406 if (err)
4407 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004408 return err;
4409}
4410
4411static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4412{
Johannes Berg4c476992010-10-04 21:36:35 +02004413 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4414 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004415
Johannes Berg4c476992010-10-04 21:36:35 +02004416 if (!rdev->ops->leave_ibss)
4417 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004418
Johannes Berg4c476992010-10-04 21:36:35 +02004419 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4420 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004421
Johannes Berg4c476992010-10-04 21:36:35 +02004422 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004423}
4424
Johannes Bergaff89a92009-07-01 21:26:51 +02004425#ifdef CONFIG_NL80211_TESTMODE
4426static struct genl_multicast_group nl80211_testmode_mcgrp = {
4427 .name = "testmode",
4428};
4429
4430static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4431{
Johannes Berg4c476992010-10-04 21:36:35 +02004432 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02004433 int err;
4434
4435 if (!info->attrs[NL80211_ATTR_TESTDATA])
4436 return -EINVAL;
4437
Johannes Bergaff89a92009-07-01 21:26:51 +02004438 err = -EOPNOTSUPP;
4439 if (rdev->ops->testmode_cmd) {
4440 rdev->testmode_info = info;
4441 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4442 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4443 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4444 rdev->testmode_info = NULL;
4445 }
4446
Johannes Bergaff89a92009-07-01 21:26:51 +02004447 return err;
4448}
4449
4450static struct sk_buff *
4451__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
4452 int approxlen, u32 pid, u32 seq, gfp_t gfp)
4453{
4454 struct sk_buff *skb;
4455 void *hdr;
4456 struct nlattr *data;
4457
4458 skb = nlmsg_new(approxlen + 100, gfp);
4459 if (!skb)
4460 return NULL;
4461
4462 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
4463 if (!hdr) {
4464 kfree_skb(skb);
4465 return NULL;
4466 }
4467
4468 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4469 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4470
4471 ((void **)skb->cb)[0] = rdev;
4472 ((void **)skb->cb)[1] = hdr;
4473 ((void **)skb->cb)[2] = data;
4474
4475 return skb;
4476
4477 nla_put_failure:
4478 kfree_skb(skb);
4479 return NULL;
4480}
4481
4482struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
4483 int approxlen)
4484{
4485 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4486
4487 if (WARN_ON(!rdev->testmode_info))
4488 return NULL;
4489
4490 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
4491 rdev->testmode_info->snd_pid,
4492 rdev->testmode_info->snd_seq,
4493 GFP_KERNEL);
4494}
4495EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4496
4497int cfg80211_testmode_reply(struct sk_buff *skb)
4498{
4499 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4500 void *hdr = ((void **)skb->cb)[1];
4501 struct nlattr *data = ((void **)skb->cb)[2];
4502
4503 if (WARN_ON(!rdev->testmode_info)) {
4504 kfree_skb(skb);
4505 return -EINVAL;
4506 }
4507
4508 nla_nest_end(skb, data);
4509 genlmsg_end(skb, hdr);
4510 return genlmsg_reply(skb, rdev->testmode_info);
4511}
4512EXPORT_SYMBOL(cfg80211_testmode_reply);
4513
4514struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4515 int approxlen, gfp_t gfp)
4516{
4517 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4518
4519 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4520}
4521EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4522
4523void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4524{
4525 void *hdr = ((void **)skb->cb)[1];
4526 struct nlattr *data = ((void **)skb->cb)[2];
4527
4528 nla_nest_end(skb, data);
4529 genlmsg_end(skb, hdr);
4530 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4531}
4532EXPORT_SYMBOL(cfg80211_testmode_event);
4533#endif
4534
Samuel Ortizb23aa672009-07-01 21:26:54 +02004535static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4536{
Johannes Berg4c476992010-10-04 21:36:35 +02004537 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4538 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004539 struct cfg80211_connect_params connect;
4540 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004541 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004542 int err;
4543
4544 memset(&connect, 0, sizeof(connect));
4545
4546 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4547 return -EINVAL;
4548
4549 if (!info->attrs[NL80211_ATTR_SSID] ||
4550 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4551 return -EINVAL;
4552
4553 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4554 connect.auth_type =
4555 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4556 if (!nl80211_valid_auth_type(connect.auth_type))
4557 return -EINVAL;
4558 } else
4559 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4560
4561 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4562
Johannes Bergc0692b82010-08-27 14:26:53 +03004563 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004564 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004565 if (err)
4566 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004567
Johannes Berg074ac8d2010-09-16 14:58:22 +02004568 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004569 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4570 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004571
Johannes Berg79c97e92009-07-07 03:56:12 +02004572 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004573
Samuel Ortizb23aa672009-07-01 21:26:54 +02004574 if (info->attrs[NL80211_ATTR_MAC])
4575 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4576 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4577 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4578
4579 if (info->attrs[NL80211_ATTR_IE]) {
4580 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4581 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4582 }
4583
4584 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4585 connect.channel =
4586 ieee80211_get_channel(wiphy,
4587 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4588 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02004589 connect.channel->flags & IEEE80211_CHAN_DISABLED)
4590 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004591 }
4592
Johannes Bergfffd0932009-07-08 14:22:54 +02004593 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4594 connkeys = nl80211_parse_connkeys(rdev,
4595 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02004596 if (IS_ERR(connkeys))
4597 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004598 }
4599
4600 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004601 if (err)
4602 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004603 return err;
4604}
4605
4606static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4607{
Johannes Berg4c476992010-10-04 21:36:35 +02004608 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4609 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004610 u16 reason;
4611
4612 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4613 reason = WLAN_REASON_DEAUTH_LEAVING;
4614 else
4615 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4616
4617 if (reason == 0)
4618 return -EINVAL;
4619
Johannes Berg074ac8d2010-09-16 14:58:22 +02004620 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004621 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4622 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004623
Johannes Berg4c476992010-10-04 21:36:35 +02004624 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004625}
4626
Johannes Berg463d0182009-07-14 00:33:35 +02004627static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4628{
Johannes Berg4c476992010-10-04 21:36:35 +02004629 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02004630 struct net *net;
4631 int err;
4632 u32 pid;
4633
4634 if (!info->attrs[NL80211_ATTR_PID])
4635 return -EINVAL;
4636
4637 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4638
Johannes Berg463d0182009-07-14 00:33:35 +02004639 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02004640 if (IS_ERR(net))
4641 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004642
4643 err = 0;
4644
4645 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02004646 if (!net_eq(wiphy_net(&rdev->wiphy), net))
4647 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02004648
Johannes Berg463d0182009-07-14 00:33:35 +02004649 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004650 return err;
4651}
4652
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004653static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4654{
Johannes Berg4c476992010-10-04 21:36:35 +02004655 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004656 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4657 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02004658 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004659 struct cfg80211_pmksa pmksa;
4660
4661 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4662
4663 if (!info->attrs[NL80211_ATTR_MAC])
4664 return -EINVAL;
4665
4666 if (!info->attrs[NL80211_ATTR_PMKID])
4667 return -EINVAL;
4668
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004669 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4670 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4671
Johannes Berg074ac8d2010-09-16 14:58:22 +02004672 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004673 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4674 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004675
4676 switch (info->genlhdr->cmd) {
4677 case NL80211_CMD_SET_PMKSA:
4678 rdev_ops = rdev->ops->set_pmksa;
4679 break;
4680 case NL80211_CMD_DEL_PMKSA:
4681 rdev_ops = rdev->ops->del_pmksa;
4682 break;
4683 default:
4684 WARN_ON(1);
4685 break;
4686 }
4687
Johannes Berg4c476992010-10-04 21:36:35 +02004688 if (!rdev_ops)
4689 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004690
Johannes Berg4c476992010-10-04 21:36:35 +02004691 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004692}
4693
4694static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
4695{
Johannes Berg4c476992010-10-04 21:36:35 +02004696 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4697 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004698
Johannes Berg074ac8d2010-09-16 14:58:22 +02004699 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004700 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4701 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004702
Johannes Berg4c476992010-10-04 21:36:35 +02004703 if (!rdev->ops->flush_pmksa)
4704 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004705
Johannes Berg4c476992010-10-04 21:36:35 +02004706 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004707}
4708
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004709static int nl80211_remain_on_channel(struct sk_buff *skb,
4710 struct genl_info *info)
4711{
Johannes Berg4c476992010-10-04 21:36:35 +02004712 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4713 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004714 struct ieee80211_channel *chan;
4715 struct sk_buff *msg;
4716 void *hdr;
4717 u64 cookie;
4718 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4719 u32 freq, duration;
4720 int err;
4721
4722 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4723 !info->attrs[NL80211_ATTR_DURATION])
4724 return -EINVAL;
4725
4726 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4727
4728 /*
4729 * We should be on that channel for at least one jiffie,
4730 * and more than 5 seconds seems excessive.
4731 */
Johannes Berga2939112010-12-14 17:54:28 +01004732 if (!duration || !msecs_to_jiffies(duration) ||
4733 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004734 return -EINVAL;
4735
Johannes Berg4c476992010-10-04 21:36:35 +02004736 if (!rdev->ops->remain_on_channel)
4737 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004738
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004739 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4740 channel_type = nla_get_u32(
4741 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4742 if (channel_type != NL80211_CHAN_NO_HT &&
4743 channel_type != NL80211_CHAN_HT20 &&
4744 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02004745 channel_type != NL80211_CHAN_HT40MINUS)
4746 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004747 }
4748
4749 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4750 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02004751 if (chan == NULL)
4752 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004753
4754 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004755 if (!msg)
4756 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004757
4758 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4759 NL80211_CMD_REMAIN_ON_CHANNEL);
4760
4761 if (IS_ERR(hdr)) {
4762 err = PTR_ERR(hdr);
4763 goto free_msg;
4764 }
4765
4766 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
4767 channel_type, duration, &cookie);
4768
4769 if (err)
4770 goto free_msg;
4771
4772 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4773
4774 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004775
4776 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004777
4778 nla_put_failure:
4779 err = -ENOBUFS;
4780 free_msg:
4781 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004782 return err;
4783}
4784
4785static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4786 struct genl_info *info)
4787{
Johannes Berg4c476992010-10-04 21:36:35 +02004788 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4789 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004790 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004791
4792 if (!info->attrs[NL80211_ATTR_COOKIE])
4793 return -EINVAL;
4794
Johannes Berg4c476992010-10-04 21:36:35 +02004795 if (!rdev->ops->cancel_remain_on_channel)
4796 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004797
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004798 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4799
Johannes Berg4c476992010-10-04 21:36:35 +02004800 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004801}
4802
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004803static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4804 u8 *rates, u8 rates_len)
4805{
4806 u8 i;
4807 u32 mask = 0;
4808
4809 for (i = 0; i < rates_len; i++) {
4810 int rate = (rates[i] & 0x7f) * 5;
4811 int ridx;
4812 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4813 struct ieee80211_rate *srate =
4814 &sband->bitrates[ridx];
4815 if (rate == srate->bitrate) {
4816 mask |= 1 << ridx;
4817 break;
4818 }
4819 }
4820 if (ridx == sband->n_bitrates)
4821 return 0; /* rate not found */
4822 }
4823
4824 return mask;
4825}
4826
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004827static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004828 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4829 .len = NL80211_MAX_SUPP_RATES },
4830};
4831
4832static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4833 struct genl_info *info)
4834{
4835 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02004836 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004837 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02004838 int rem, i;
4839 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004840 struct nlattr *tx_rates;
4841 struct ieee80211_supported_band *sband;
4842
4843 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4844 return -EINVAL;
4845
Johannes Berg4c476992010-10-04 21:36:35 +02004846 if (!rdev->ops->set_bitrate_mask)
4847 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004848
4849 memset(&mask, 0, sizeof(mask));
4850 /* Default to all rates enabled */
4851 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4852 sband = rdev->wiphy.bands[i];
4853 mask.control[i].legacy =
4854 sband ? (1 << sband->n_bitrates) - 1 : 0;
4855 }
4856
4857 /*
4858 * The nested attribute uses enum nl80211_band as the index. This maps
4859 * directly to the enum ieee80211_band values used in cfg80211.
4860 */
4861 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4862 {
4863 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02004864 if (band < 0 || band >= IEEE80211_NUM_BANDS)
4865 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004866 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02004867 if (sband == NULL)
4868 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004869 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4870 nla_len(tx_rates), nl80211_txattr_policy);
4871 if (tb[NL80211_TXRATE_LEGACY]) {
4872 mask.control[band].legacy = rateset_to_mask(
4873 sband,
4874 nla_data(tb[NL80211_TXRATE_LEGACY]),
4875 nla_len(tb[NL80211_TXRATE_LEGACY]));
Johannes Berg4c476992010-10-04 21:36:35 +02004876 if (mask.control[band].legacy == 0)
4877 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004878 }
4879 }
4880
Johannes Berg4c476992010-10-04 21:36:35 +02004881 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004882}
4883
Johannes Berg2e161f72010-08-12 15:38:38 +02004884static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004885{
Johannes Berg4c476992010-10-04 21:36:35 +02004886 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4887 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02004888 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02004889
4890 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4891 return -EINVAL;
4892
Johannes Berg2e161f72010-08-12 15:38:38 +02004893 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
4894 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02004895
Johannes Berg9d38d852010-06-09 17:20:33 +02004896 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004897 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004898 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4899 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4900 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08004901 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02004902 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4903 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004904
4905 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02004906 if (!rdev->ops->mgmt_tx)
4907 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004908
Johannes Berg4c476992010-10-04 21:36:35 +02004909 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02004910 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02004911 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
4912 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02004913}
4914
Johannes Berg2e161f72010-08-12 15:38:38 +02004915static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004916{
Johannes Berg4c476992010-10-04 21:36:35 +02004917 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4918 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02004919 struct ieee80211_channel *chan;
4920 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02004921 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02004922 u32 freq;
4923 int err;
4924 void *hdr;
4925 u64 cookie;
4926 struct sk_buff *msg;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004927 unsigned int wait = 0;
4928 bool offchan;
Jouni Malinen026331c2010-02-15 12:53:10 +02004929
4930 if (!info->attrs[NL80211_ATTR_FRAME] ||
4931 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
4932 return -EINVAL;
4933
Johannes Berg4c476992010-10-04 21:36:35 +02004934 if (!rdev->ops->mgmt_tx)
4935 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004936
Johannes Berg9d38d852010-06-09 17:20:33 +02004937 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004938 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004939 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
4940 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
4941 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08004942 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02004943 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
4944 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02004945
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004946 if (info->attrs[NL80211_ATTR_DURATION]) {
4947 if (!rdev->ops->mgmt_tx_cancel_wait)
4948 return -EINVAL;
4949 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4950 }
4951
Jouni Malinen026331c2010-02-15 12:53:10 +02004952 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4953 channel_type = nla_get_u32(
4954 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4955 if (channel_type != NL80211_CHAN_NO_HT &&
4956 channel_type != NL80211_CHAN_HT20 &&
4957 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02004958 channel_type != NL80211_CHAN_HT40MINUS)
4959 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02004960 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02004961 }
4962
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004963 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
4964
Jouni Malinen026331c2010-02-15 12:53:10 +02004965 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4966 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02004967 if (chan == NULL)
4968 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02004969
4970 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004971 if (!msg)
4972 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02004973
4974 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg2e161f72010-08-12 15:38:38 +02004975 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02004976
4977 if (IS_ERR(hdr)) {
4978 err = PTR_ERR(hdr);
4979 goto free_msg;
4980 }
Johannes Bergf7ca38d2010-11-25 10:02:29 +01004981 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
4982 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02004983 nla_data(info->attrs[NL80211_ATTR_FRAME]),
4984 nla_len(info->attrs[NL80211_ATTR_FRAME]),
4985 &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02004986 if (err)
4987 goto free_msg;
4988
4989 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4990
4991 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004992 return genlmsg_reply(msg, info);
Jouni Malinen026331c2010-02-15 12:53:10 +02004993
4994 nla_put_failure:
4995 err = -ENOBUFS;
4996 free_msg:
4997 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02004998 return err;
4999}
5000
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005001static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5002{
5003 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5004 struct net_device *dev = info->user_ptr[1];
5005 u64 cookie;
5006
5007 if (!info->attrs[NL80211_ATTR_COOKIE])
5008 return -EINVAL;
5009
5010 if (!rdev->ops->mgmt_tx_cancel_wait)
5011 return -EOPNOTSUPP;
5012
5013 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5014 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5015 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5016 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5017 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5018 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5019 return -EOPNOTSUPP;
5020
5021 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5022
5023 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5024}
5025
Kalle Valoffb9eb32010-02-17 17:58:10 +02005026static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5027{
Johannes Berg4c476992010-10-04 21:36:35 +02005028 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005029 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005030 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005031 u8 ps_state;
5032 bool state;
5033 int err;
5034
Johannes Berg4c476992010-10-04 21:36:35 +02005035 if (!info->attrs[NL80211_ATTR_PS_STATE])
5036 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005037
5038 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5039
Johannes Berg4c476992010-10-04 21:36:35 +02005040 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5041 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005042
5043 wdev = dev->ieee80211_ptr;
5044
Johannes Berg4c476992010-10-04 21:36:35 +02005045 if (!rdev->ops->set_power_mgmt)
5046 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005047
5048 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5049
5050 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005051 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005052
Johannes Berg4c476992010-10-04 21:36:35 +02005053 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5054 wdev->ps_timeout);
5055 if (!err)
5056 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005057 return err;
5058}
5059
5060static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5061{
Johannes Berg4c476992010-10-04 21:36:35 +02005062 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005063 enum nl80211_ps_state ps_state;
5064 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005065 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005066 struct sk_buff *msg;
5067 void *hdr;
5068 int err;
5069
Kalle Valoffb9eb32010-02-17 17:58:10 +02005070 wdev = dev->ieee80211_ptr;
5071
Johannes Berg4c476992010-10-04 21:36:35 +02005072 if (!rdev->ops->set_power_mgmt)
5073 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005074
5075 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005076 if (!msg)
5077 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005078
5079 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5080 NL80211_CMD_GET_POWER_SAVE);
5081 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005082 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005083 goto free_msg;
5084 }
5085
5086 if (wdev->ps)
5087 ps_state = NL80211_PS_ENABLED;
5088 else
5089 ps_state = NL80211_PS_DISABLED;
5090
5091 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5092
5093 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005094 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005095
Johannes Berg4c476992010-10-04 21:36:35 +02005096 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005097 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005098 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005099 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005100 return err;
5101}
5102
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005103static struct nla_policy
5104nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5105 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5106 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5107 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5108};
5109
5110static int nl80211_set_cqm_rssi(struct genl_info *info,
5111 s32 threshold, u32 hysteresis)
5112{
Johannes Berg4c476992010-10-04 21:36:35 +02005113 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005114 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005115 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005116
5117 if (threshold > 0)
5118 return -EINVAL;
5119
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005120 wdev = dev->ieee80211_ptr;
5121
Johannes Berg4c476992010-10-04 21:36:35 +02005122 if (!rdev->ops->set_cqm_rssi_config)
5123 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005124
Johannes Berg074ac8d2010-09-16 14:58:22 +02005125 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005126 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
5127 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005128
Johannes Berg4c476992010-10-04 21:36:35 +02005129 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5130 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005131}
5132
5133static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5134{
5135 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5136 struct nlattr *cqm;
5137 int err;
5138
5139 cqm = info->attrs[NL80211_ATTR_CQM];
5140 if (!cqm) {
5141 err = -EINVAL;
5142 goto out;
5143 }
5144
5145 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5146 nl80211_attr_cqm_policy);
5147 if (err)
5148 goto out;
5149
5150 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5151 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5152 s32 threshold;
5153 u32 hysteresis;
5154 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5155 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5156 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5157 } else
5158 err = -EINVAL;
5159
5160out:
5161 return err;
5162}
5163
Johannes Berg29cbe682010-12-03 09:20:44 +01005164static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
5165{
5166 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5167 struct net_device *dev = info->user_ptr[1];
5168 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08005169 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01005170 int err;
5171
5172 /* start with default */
5173 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08005174 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01005175
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005176 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01005177 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005178 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01005179 if (err)
5180 return err;
5181 }
5182
5183 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
5184 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
5185 return -EINVAL;
5186
Javier Cardonac80d5452010-12-16 17:37:49 -08005187 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
5188 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
5189
5190 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
5191 /* parse additional setup parameters if given */
5192 err = nl80211_parse_mesh_setup(info, &setup);
5193 if (err)
5194 return err;
5195 }
5196
5197 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01005198}
5199
5200static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
5201{
5202 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5203 struct net_device *dev = info->user_ptr[1];
5204
5205 return cfg80211_leave_mesh(rdev, dev);
5206}
5207
Johannes Bergff1b6e62011-05-04 15:37:28 +02005208static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
5209{
5210 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5211 struct sk_buff *msg;
5212 void *hdr;
5213
5214 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5215 return -EOPNOTSUPP;
5216
5217 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5218 if (!msg)
5219 return -ENOMEM;
5220
5221 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5222 NL80211_CMD_GET_WOWLAN);
5223 if (!hdr)
5224 goto nla_put_failure;
5225
5226 if (rdev->wowlan) {
5227 struct nlattr *nl_wowlan;
5228
5229 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
5230 if (!nl_wowlan)
5231 goto nla_put_failure;
5232
5233 if (rdev->wowlan->any)
5234 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
5235 if (rdev->wowlan->disconnect)
5236 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
5237 if (rdev->wowlan->magic_pkt)
5238 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
5239 if (rdev->wowlan->n_patterns) {
5240 struct nlattr *nl_pats, *nl_pat;
5241 int i, pat_len;
5242
5243 nl_pats = nla_nest_start(msg,
5244 NL80211_WOWLAN_TRIG_PKT_PATTERN);
5245 if (!nl_pats)
5246 goto nla_put_failure;
5247
5248 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
5249 nl_pat = nla_nest_start(msg, i + 1);
5250 if (!nl_pat)
5251 goto nla_put_failure;
5252 pat_len = rdev->wowlan->patterns[i].pattern_len;
5253 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
5254 DIV_ROUND_UP(pat_len, 8),
5255 rdev->wowlan->patterns[i].mask);
5256 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
5257 pat_len,
5258 rdev->wowlan->patterns[i].pattern);
5259 nla_nest_end(msg, nl_pat);
5260 }
5261 nla_nest_end(msg, nl_pats);
5262 }
5263
5264 nla_nest_end(msg, nl_wowlan);
5265 }
5266
5267 genlmsg_end(msg, hdr);
5268 return genlmsg_reply(msg, info);
5269
5270nla_put_failure:
5271 nlmsg_free(msg);
5272 return -ENOBUFS;
5273}
5274
5275static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
5276{
5277 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5278 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
5279 struct cfg80211_wowlan no_triggers = {};
5280 struct cfg80211_wowlan new_triggers = {};
5281 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
5282 int err, i;
5283
5284 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5285 return -EOPNOTSUPP;
5286
5287 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
5288 goto no_triggers;
5289
5290 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
5291 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5292 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5293 nl80211_wowlan_policy);
5294 if (err)
5295 return err;
5296
5297 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
5298 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
5299 return -EINVAL;
5300 new_triggers.any = true;
5301 }
5302
5303 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
5304 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
5305 return -EINVAL;
5306 new_triggers.disconnect = true;
5307 }
5308
5309 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
5310 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
5311 return -EINVAL;
5312 new_triggers.magic_pkt = true;
5313 }
5314
5315 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
5316 struct nlattr *pat;
5317 int n_patterns = 0;
5318 int rem, pat_len, mask_len;
5319 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
5320
5321 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5322 rem)
5323 n_patterns++;
5324 if (n_patterns > wowlan->n_patterns)
5325 return -EINVAL;
5326
5327 new_triggers.patterns = kcalloc(n_patterns,
5328 sizeof(new_triggers.patterns[0]),
5329 GFP_KERNEL);
5330 if (!new_triggers.patterns)
5331 return -ENOMEM;
5332
5333 new_triggers.n_patterns = n_patterns;
5334 i = 0;
5335
5336 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5337 rem) {
5338 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
5339 nla_data(pat), nla_len(pat), NULL);
5340 err = -EINVAL;
5341 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
5342 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
5343 goto error;
5344 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
5345 mask_len = DIV_ROUND_UP(pat_len, 8);
5346 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
5347 mask_len)
5348 goto error;
5349 if (pat_len > wowlan->pattern_max_len ||
5350 pat_len < wowlan->pattern_min_len)
5351 goto error;
5352
5353 new_triggers.patterns[i].mask =
5354 kmalloc(mask_len + pat_len, GFP_KERNEL);
5355 if (!new_triggers.patterns[i].mask) {
5356 err = -ENOMEM;
5357 goto error;
5358 }
5359 new_triggers.patterns[i].pattern =
5360 new_triggers.patterns[i].mask + mask_len;
5361 memcpy(new_triggers.patterns[i].mask,
5362 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
5363 mask_len);
5364 new_triggers.patterns[i].pattern_len = pat_len;
5365 memcpy(new_triggers.patterns[i].pattern,
5366 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
5367 pat_len);
5368 i++;
5369 }
5370 }
5371
5372 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
5373 struct cfg80211_wowlan *ntrig;
5374 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
5375 GFP_KERNEL);
5376 if (!ntrig) {
5377 err = -ENOMEM;
5378 goto error;
5379 }
5380 cfg80211_rdev_free_wowlan(rdev);
5381 rdev->wowlan = ntrig;
5382 } else {
5383 no_triggers:
5384 cfg80211_rdev_free_wowlan(rdev);
5385 rdev->wowlan = NULL;
5386 }
5387
5388 return 0;
5389 error:
5390 for (i = 0; i < new_triggers.n_patterns; i++)
5391 kfree(new_triggers.patterns[i].mask);
5392 kfree(new_triggers.patterns);
5393 return err;
5394}
5395
Johannes Berg4c476992010-10-04 21:36:35 +02005396#define NL80211_FLAG_NEED_WIPHY 0x01
5397#define NL80211_FLAG_NEED_NETDEV 0x02
5398#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02005399#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
5400#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
5401 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02005402
5403static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
5404 struct genl_info *info)
5405{
5406 struct cfg80211_registered_device *rdev;
5407 struct net_device *dev;
5408 int err;
5409 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
5410
5411 if (rtnl)
5412 rtnl_lock();
5413
5414 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
5415 rdev = cfg80211_get_dev_from_info(info);
5416 if (IS_ERR(rdev)) {
5417 if (rtnl)
5418 rtnl_unlock();
5419 return PTR_ERR(rdev);
5420 }
5421 info->user_ptr[0] = rdev;
5422 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
5423 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5424 if (err) {
5425 if (rtnl)
5426 rtnl_unlock();
5427 return err;
5428 }
Johannes Berg41265712010-10-04 21:14:05 +02005429 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
5430 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02005431 cfg80211_unlock_rdev(rdev);
5432 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02005433 if (rtnl)
5434 rtnl_unlock();
5435 return -ENETDOWN;
5436 }
Johannes Berg4c476992010-10-04 21:36:35 +02005437 info->user_ptr[0] = rdev;
5438 info->user_ptr[1] = dev;
5439 }
5440
5441 return 0;
5442}
5443
5444static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
5445 struct genl_info *info)
5446{
5447 if (info->user_ptr[0])
5448 cfg80211_unlock_rdev(info->user_ptr[0]);
5449 if (info->user_ptr[1])
5450 dev_put(info->user_ptr[1]);
5451 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
5452 rtnl_unlock();
5453}
5454
Johannes Berg55682962007-09-20 13:09:35 -04005455static struct genl_ops nl80211_ops[] = {
5456 {
5457 .cmd = NL80211_CMD_GET_WIPHY,
5458 .doit = nl80211_get_wiphy,
5459 .dumpit = nl80211_dump_wiphy,
5460 .policy = nl80211_policy,
5461 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005462 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04005463 },
5464 {
5465 .cmd = NL80211_CMD_SET_WIPHY,
5466 .doit = nl80211_set_wiphy,
5467 .policy = nl80211_policy,
5468 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005469 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005470 },
5471 {
5472 .cmd = NL80211_CMD_GET_INTERFACE,
5473 .doit = nl80211_get_interface,
5474 .dumpit = nl80211_dump_interface,
5475 .policy = nl80211_policy,
5476 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005477 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04005478 },
5479 {
5480 .cmd = NL80211_CMD_SET_INTERFACE,
5481 .doit = nl80211_set_interface,
5482 .policy = nl80211_policy,
5483 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005484 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5485 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005486 },
5487 {
5488 .cmd = NL80211_CMD_NEW_INTERFACE,
5489 .doit = nl80211_new_interface,
5490 .policy = nl80211_policy,
5491 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005492 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5493 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005494 },
5495 {
5496 .cmd = NL80211_CMD_DEL_INTERFACE,
5497 .doit = nl80211_del_interface,
5498 .policy = nl80211_policy,
5499 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005500 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5501 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005502 },
Johannes Berg41ade002007-12-19 02:03:29 +01005503 {
5504 .cmd = NL80211_CMD_GET_KEY,
5505 .doit = nl80211_get_key,
5506 .policy = nl80211_policy,
5507 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005508 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5509 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005510 },
5511 {
5512 .cmd = NL80211_CMD_SET_KEY,
5513 .doit = nl80211_set_key,
5514 .policy = nl80211_policy,
5515 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005516 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005517 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005518 },
5519 {
5520 .cmd = NL80211_CMD_NEW_KEY,
5521 .doit = nl80211_new_key,
5522 .policy = nl80211_policy,
5523 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005524 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005525 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005526 },
5527 {
5528 .cmd = NL80211_CMD_DEL_KEY,
5529 .doit = nl80211_del_key,
5530 .policy = nl80211_policy,
5531 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005532 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005533 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005534 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01005535 {
5536 .cmd = NL80211_CMD_SET_BEACON,
5537 .policy = nl80211_policy,
5538 .flags = GENL_ADMIN_PERM,
5539 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005540 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5541 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005542 },
5543 {
5544 .cmd = NL80211_CMD_NEW_BEACON,
5545 .policy = nl80211_policy,
5546 .flags = GENL_ADMIN_PERM,
5547 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005548 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5549 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005550 },
5551 {
5552 .cmd = NL80211_CMD_DEL_BEACON,
5553 .policy = nl80211_policy,
5554 .flags = GENL_ADMIN_PERM,
5555 .doit = nl80211_del_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005556 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5557 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005558 },
Johannes Berg5727ef12007-12-19 02:03:34 +01005559 {
5560 .cmd = NL80211_CMD_GET_STATION,
5561 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005562 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01005563 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02005564 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5565 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005566 },
5567 {
5568 .cmd = NL80211_CMD_SET_STATION,
5569 .doit = nl80211_set_station,
5570 .policy = nl80211_policy,
5571 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005572 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5573 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005574 },
5575 {
5576 .cmd = NL80211_CMD_NEW_STATION,
5577 .doit = nl80211_new_station,
5578 .policy = nl80211_policy,
5579 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005580 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005581 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005582 },
5583 {
5584 .cmd = NL80211_CMD_DEL_STATION,
5585 .doit = nl80211_del_station,
5586 .policy = nl80211_policy,
5587 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005588 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5589 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005590 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005591 {
5592 .cmd = NL80211_CMD_GET_MPATH,
5593 .doit = nl80211_get_mpath,
5594 .dumpit = nl80211_dump_mpath,
5595 .policy = nl80211_policy,
5596 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005597 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005598 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005599 },
5600 {
5601 .cmd = NL80211_CMD_SET_MPATH,
5602 .doit = nl80211_set_mpath,
5603 .policy = nl80211_policy,
5604 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005605 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005606 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005607 },
5608 {
5609 .cmd = NL80211_CMD_NEW_MPATH,
5610 .doit = nl80211_new_mpath,
5611 .policy = nl80211_policy,
5612 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005613 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005614 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005615 },
5616 {
5617 .cmd = NL80211_CMD_DEL_MPATH,
5618 .doit = nl80211_del_mpath,
5619 .policy = nl80211_policy,
5620 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005621 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5622 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005623 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005624 {
5625 .cmd = NL80211_CMD_SET_BSS,
5626 .doit = nl80211_set_bss,
5627 .policy = nl80211_policy,
5628 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005629 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5630 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005631 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005632 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08005633 .cmd = NL80211_CMD_GET_REG,
5634 .doit = nl80211_get_reg,
5635 .policy = nl80211_policy,
5636 /* can be retrieved by unprivileged users */
5637 },
5638 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005639 .cmd = NL80211_CMD_SET_REG,
5640 .doit = nl80211_set_reg,
5641 .policy = nl80211_policy,
5642 .flags = GENL_ADMIN_PERM,
5643 },
5644 {
5645 .cmd = NL80211_CMD_REQ_SET_REG,
5646 .doit = nl80211_req_set_reg,
5647 .policy = nl80211_policy,
5648 .flags = GENL_ADMIN_PERM,
5649 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005650 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005651 .cmd = NL80211_CMD_GET_MESH_CONFIG,
5652 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005653 .policy = nl80211_policy,
5654 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005655 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5656 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005657 },
5658 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005659 .cmd = NL80211_CMD_SET_MESH_CONFIG,
5660 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005661 .policy = nl80211_policy,
5662 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01005663 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005664 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005665 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02005666 {
Johannes Berg2a519312009-02-10 21:25:55 +01005667 .cmd = NL80211_CMD_TRIGGER_SCAN,
5668 .doit = nl80211_trigger_scan,
5669 .policy = nl80211_policy,
5670 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005671 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005672 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01005673 },
5674 {
5675 .cmd = NL80211_CMD_GET_SCAN,
5676 .policy = nl80211_policy,
5677 .dumpit = nl80211_dump_scan,
5678 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02005679 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03005680 .cmd = NL80211_CMD_START_SCHED_SCAN,
5681 .doit = nl80211_start_sched_scan,
5682 .policy = nl80211_policy,
5683 .flags = GENL_ADMIN_PERM,
5684 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5685 NL80211_FLAG_NEED_RTNL,
5686 },
5687 {
5688 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
5689 .doit = nl80211_stop_sched_scan,
5690 .policy = nl80211_policy,
5691 .flags = GENL_ADMIN_PERM,
5692 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5693 NL80211_FLAG_NEED_RTNL,
5694 },
5695 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02005696 .cmd = NL80211_CMD_AUTHENTICATE,
5697 .doit = nl80211_authenticate,
5698 .policy = nl80211_policy,
5699 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005700 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005701 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005702 },
5703 {
5704 .cmd = NL80211_CMD_ASSOCIATE,
5705 .doit = nl80211_associate,
5706 .policy = nl80211_policy,
5707 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005708 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005709 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005710 },
5711 {
5712 .cmd = NL80211_CMD_DEAUTHENTICATE,
5713 .doit = nl80211_deauthenticate,
5714 .policy = nl80211_policy,
5715 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005716 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005717 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005718 },
5719 {
5720 .cmd = NL80211_CMD_DISASSOCIATE,
5721 .doit = nl80211_disassociate,
5722 .policy = nl80211_policy,
5723 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005724 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005725 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005726 },
Johannes Berg04a773a2009-04-19 21:24:32 +02005727 {
5728 .cmd = NL80211_CMD_JOIN_IBSS,
5729 .doit = nl80211_join_ibss,
5730 .policy = nl80211_policy,
5731 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005732 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005733 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02005734 },
5735 {
5736 .cmd = NL80211_CMD_LEAVE_IBSS,
5737 .doit = nl80211_leave_ibss,
5738 .policy = nl80211_policy,
5739 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005740 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005741 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02005742 },
Johannes Bergaff89a92009-07-01 21:26:51 +02005743#ifdef CONFIG_NL80211_TESTMODE
5744 {
5745 .cmd = NL80211_CMD_TESTMODE,
5746 .doit = nl80211_testmode_do,
5747 .policy = nl80211_policy,
5748 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005749 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5750 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02005751 },
5752#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02005753 {
5754 .cmd = NL80211_CMD_CONNECT,
5755 .doit = nl80211_connect,
5756 .policy = nl80211_policy,
5757 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005758 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005759 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005760 },
5761 {
5762 .cmd = NL80211_CMD_DISCONNECT,
5763 .doit = nl80211_disconnect,
5764 .policy = nl80211_policy,
5765 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005766 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005767 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005768 },
Johannes Berg463d0182009-07-14 00:33:35 +02005769 {
5770 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
5771 .doit = nl80211_wiphy_netns,
5772 .policy = nl80211_policy,
5773 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005774 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5775 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02005776 },
Holger Schurig61fa7132009-11-11 12:25:40 +01005777 {
5778 .cmd = NL80211_CMD_GET_SURVEY,
5779 .policy = nl80211_policy,
5780 .dumpit = nl80211_dump_survey,
5781 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005782 {
5783 .cmd = NL80211_CMD_SET_PMKSA,
5784 .doit = nl80211_setdel_pmksa,
5785 .policy = nl80211_policy,
5786 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005787 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5788 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005789 },
5790 {
5791 .cmd = NL80211_CMD_DEL_PMKSA,
5792 .doit = nl80211_setdel_pmksa,
5793 .policy = nl80211_policy,
5794 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005795 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5796 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005797 },
5798 {
5799 .cmd = NL80211_CMD_FLUSH_PMKSA,
5800 .doit = nl80211_flush_pmksa,
5801 .policy = nl80211_policy,
5802 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005803 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5804 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005805 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005806 {
5807 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
5808 .doit = nl80211_remain_on_channel,
5809 .policy = nl80211_policy,
5810 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005811 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005812 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005813 },
5814 {
5815 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
5816 .doit = nl80211_cancel_remain_on_channel,
5817 .policy = nl80211_policy,
5818 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005819 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005820 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005821 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005822 {
5823 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
5824 .doit = nl80211_set_tx_bitrate_mask,
5825 .policy = nl80211_policy,
5826 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005827 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5828 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005829 },
Jouni Malinen026331c2010-02-15 12:53:10 +02005830 {
Johannes Berg2e161f72010-08-12 15:38:38 +02005831 .cmd = NL80211_CMD_REGISTER_FRAME,
5832 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02005833 .policy = nl80211_policy,
5834 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005835 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5836 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02005837 },
5838 {
Johannes Berg2e161f72010-08-12 15:38:38 +02005839 .cmd = NL80211_CMD_FRAME,
5840 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02005841 .policy = nl80211_policy,
5842 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005843 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005844 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02005845 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02005846 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005847 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
5848 .doit = nl80211_tx_mgmt_cancel_wait,
5849 .policy = nl80211_policy,
5850 .flags = GENL_ADMIN_PERM,
5851 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5852 NL80211_FLAG_NEED_RTNL,
5853 },
5854 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02005855 .cmd = NL80211_CMD_SET_POWER_SAVE,
5856 .doit = nl80211_set_power_save,
5857 .policy = nl80211_policy,
5858 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005859 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5860 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02005861 },
5862 {
5863 .cmd = NL80211_CMD_GET_POWER_SAVE,
5864 .doit = nl80211_get_power_save,
5865 .policy = nl80211_policy,
5866 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005867 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5868 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02005869 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005870 {
5871 .cmd = NL80211_CMD_SET_CQM,
5872 .doit = nl80211_set_cqm,
5873 .policy = nl80211_policy,
5874 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005875 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5876 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005877 },
Johannes Bergf444de02010-05-05 15:25:02 +02005878 {
5879 .cmd = NL80211_CMD_SET_CHANNEL,
5880 .doit = nl80211_set_channel,
5881 .policy = nl80211_policy,
5882 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005883 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5884 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02005885 },
Bill Jordane8347eb2010-10-01 13:54:28 -04005886 {
5887 .cmd = NL80211_CMD_SET_WDS_PEER,
5888 .doit = nl80211_set_wds_peer,
5889 .policy = nl80211_policy,
5890 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02005891 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5892 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04005893 },
Johannes Berg29cbe682010-12-03 09:20:44 +01005894 {
5895 .cmd = NL80211_CMD_JOIN_MESH,
5896 .doit = nl80211_join_mesh,
5897 .policy = nl80211_policy,
5898 .flags = GENL_ADMIN_PERM,
5899 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5900 NL80211_FLAG_NEED_RTNL,
5901 },
5902 {
5903 .cmd = NL80211_CMD_LEAVE_MESH,
5904 .doit = nl80211_leave_mesh,
5905 .policy = nl80211_policy,
5906 .flags = GENL_ADMIN_PERM,
5907 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5908 NL80211_FLAG_NEED_RTNL,
5909 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02005910 {
5911 .cmd = NL80211_CMD_GET_WOWLAN,
5912 .doit = nl80211_get_wowlan,
5913 .policy = nl80211_policy,
5914 /* can be retrieved by unprivileged users */
5915 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5916 NL80211_FLAG_NEED_RTNL,
5917 },
5918 {
5919 .cmd = NL80211_CMD_SET_WOWLAN,
5920 .doit = nl80211_set_wowlan,
5921 .policy = nl80211_policy,
5922 .flags = GENL_ADMIN_PERM,
5923 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5924 NL80211_FLAG_NEED_RTNL,
5925 },
Johannes Berg55682962007-09-20 13:09:35 -04005926};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005927
Jouni Malinen6039f6d2009-03-19 13:39:21 +02005928static struct genl_multicast_group nl80211_mlme_mcgrp = {
5929 .name = "mlme",
5930};
Johannes Berg55682962007-09-20 13:09:35 -04005931
5932/* multicast groups */
5933static struct genl_multicast_group nl80211_config_mcgrp = {
5934 .name = "config",
5935};
Johannes Berg2a519312009-02-10 21:25:55 +01005936static struct genl_multicast_group nl80211_scan_mcgrp = {
5937 .name = "scan",
5938};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04005939static struct genl_multicast_group nl80211_regulatory_mcgrp = {
5940 .name = "regulatory",
5941};
Johannes Berg55682962007-09-20 13:09:35 -04005942
5943/* notification functions */
5944
5945void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
5946{
5947 struct sk_buff *msg;
5948
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07005949 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005950 if (!msg)
5951 return;
5952
5953 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
5954 nlmsg_free(msg);
5955 return;
5956 }
5957
Johannes Berg463d0182009-07-14 00:33:35 +02005958 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
5959 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04005960}
5961
Johannes Berg362a4152009-05-24 16:43:15 +02005962static int nl80211_add_scan_req(struct sk_buff *msg,
5963 struct cfg80211_registered_device *rdev)
5964{
5965 struct cfg80211_scan_request *req = rdev->scan_req;
5966 struct nlattr *nest;
5967 int i;
5968
Johannes Berg667503d2009-07-07 03:56:11 +02005969 ASSERT_RDEV_LOCK(rdev);
5970
Johannes Berg362a4152009-05-24 16:43:15 +02005971 if (WARN_ON(!req))
5972 return 0;
5973
5974 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
5975 if (!nest)
5976 goto nla_put_failure;
5977 for (i = 0; i < req->n_ssids; i++)
5978 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
5979 nla_nest_end(msg, nest);
5980
5981 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
5982 if (!nest)
5983 goto nla_put_failure;
5984 for (i = 0; i < req->n_channels; i++)
5985 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
5986 nla_nest_end(msg, nest);
5987
5988 if (req->ie)
5989 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
5990
5991 return 0;
5992 nla_put_failure:
5993 return -ENOBUFS;
5994}
5995
Johannes Berga538e2d2009-06-16 19:56:42 +02005996static int nl80211_send_scan_msg(struct sk_buff *msg,
5997 struct cfg80211_registered_device *rdev,
5998 struct net_device *netdev,
5999 u32 pid, u32 seq, int flags,
6000 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01006001{
6002 void *hdr;
6003
6004 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6005 if (!hdr)
6006 return -1;
6007
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05006008 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01006009 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6010
Johannes Berg362a4152009-05-24 16:43:15 +02006011 /* ignore errors and send incomplete event anyway */
6012 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01006013
6014 return genlmsg_end(msg, hdr);
6015
6016 nla_put_failure:
6017 genlmsg_cancel(msg, hdr);
6018 return -EMSGSIZE;
6019}
6020
Luciano Coelho807f8a82011-05-11 17:09:35 +03006021static int
6022nl80211_send_sched_scan_msg(struct sk_buff *msg,
6023 struct cfg80211_registered_device *rdev,
6024 struct net_device *netdev,
6025 u32 pid, u32 seq, int flags, u32 cmd)
6026{
6027 void *hdr;
6028
6029 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6030 if (!hdr)
6031 return -1;
6032
6033 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6034 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6035
6036 return genlmsg_end(msg, hdr);
6037
6038 nla_put_failure:
6039 genlmsg_cancel(msg, hdr);
6040 return -EMSGSIZE;
6041}
6042
Johannes Berga538e2d2009-06-16 19:56:42 +02006043void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
6044 struct net_device *netdev)
6045{
6046 struct sk_buff *msg;
6047
6048 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6049 if (!msg)
6050 return;
6051
6052 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6053 NL80211_CMD_TRIGGER_SCAN) < 0) {
6054 nlmsg_free(msg);
6055 return;
6056 }
6057
Johannes Berg463d0182009-07-14 00:33:35 +02006058 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6059 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02006060}
6061
Johannes Berg2a519312009-02-10 21:25:55 +01006062void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
6063 struct net_device *netdev)
6064{
6065 struct sk_buff *msg;
6066
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006067 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006068 if (!msg)
6069 return;
6070
Johannes Berga538e2d2009-06-16 19:56:42 +02006071 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6072 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006073 nlmsg_free(msg);
6074 return;
6075 }
6076
Johannes Berg463d0182009-07-14 00:33:35 +02006077 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6078 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006079}
6080
6081void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
6082 struct net_device *netdev)
6083{
6084 struct sk_buff *msg;
6085
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006086 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006087 if (!msg)
6088 return;
6089
Johannes Berga538e2d2009-06-16 19:56:42 +02006090 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6091 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006092 nlmsg_free(msg);
6093 return;
6094 }
6095
Johannes Berg463d0182009-07-14 00:33:35 +02006096 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6097 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006098}
6099
Luciano Coelho807f8a82011-05-11 17:09:35 +03006100void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
6101 struct net_device *netdev)
6102{
6103 struct sk_buff *msg;
6104
6105 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6106 if (!msg)
6107 return;
6108
6109 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
6110 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
6111 nlmsg_free(msg);
6112 return;
6113 }
6114
6115 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6116 nl80211_scan_mcgrp.id, GFP_KERNEL);
6117}
6118
6119void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
6120 struct net_device *netdev, u32 cmd)
6121{
6122 struct sk_buff *msg;
6123
6124 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6125 if (!msg)
6126 return;
6127
6128 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
6129 nlmsg_free(msg);
6130 return;
6131 }
6132
6133 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6134 nl80211_scan_mcgrp.id, GFP_KERNEL);
6135}
6136
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006137/*
6138 * This can happen on global regulatory changes or device specific settings
6139 * based on custom world regulatory domains.
6140 */
6141void nl80211_send_reg_change_event(struct regulatory_request *request)
6142{
6143 struct sk_buff *msg;
6144 void *hdr;
6145
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006146 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006147 if (!msg)
6148 return;
6149
6150 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
6151 if (!hdr) {
6152 nlmsg_free(msg);
6153 return;
6154 }
6155
6156 /* Userspace can always count this one always being set */
6157 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
6158
6159 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
6160 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6161 NL80211_REGDOM_TYPE_WORLD);
6162 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
6163 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6164 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
6165 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
6166 request->intersect)
6167 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6168 NL80211_REGDOM_TYPE_INTERSECTION);
6169 else {
6170 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6171 NL80211_REGDOM_TYPE_COUNTRY);
6172 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
6173 }
6174
6175 if (wiphy_idx_valid(request->wiphy_idx))
6176 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
6177
6178 if (genlmsg_end(msg, hdr) < 0) {
6179 nlmsg_free(msg);
6180 return;
6181 }
6182
Johannes Bergbc43b282009-07-25 10:54:13 +02006183 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02006184 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02006185 GFP_ATOMIC);
6186 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006187
6188 return;
6189
6190nla_put_failure:
6191 genlmsg_cancel(msg, hdr);
6192 nlmsg_free(msg);
6193}
6194
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006195static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
6196 struct net_device *netdev,
6197 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006198 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006199{
6200 struct sk_buff *msg;
6201 void *hdr;
6202
Johannes Berge6d6e342009-07-01 21:26:47 +02006203 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006204 if (!msg)
6205 return;
6206
6207 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6208 if (!hdr) {
6209 nlmsg_free(msg);
6210 return;
6211 }
6212
6213 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6214 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6215 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6216
6217 if (genlmsg_end(msg, hdr) < 0) {
6218 nlmsg_free(msg);
6219 return;
6220 }
6221
Johannes Berg463d0182009-07-14 00:33:35 +02006222 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6223 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006224 return;
6225
6226 nla_put_failure:
6227 genlmsg_cancel(msg, hdr);
6228 nlmsg_free(msg);
6229}
6230
6231void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006232 struct net_device *netdev, const u8 *buf,
6233 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006234{
6235 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006236 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006237}
6238
6239void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
6240 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006241 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006242{
Johannes Berge6d6e342009-07-01 21:26:47 +02006243 nl80211_send_mlme_event(rdev, netdev, buf, len,
6244 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006245}
6246
Jouni Malinen53b46b82009-03-27 20:53:56 +02006247void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006248 struct net_device *netdev, const u8 *buf,
6249 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006250{
6251 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006252 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006253}
6254
Jouni Malinen53b46b82009-03-27 20:53:56 +02006255void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
6256 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006257 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006258{
6259 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006260 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006261}
6262
Jouni Malinencf4e5942010-12-16 00:52:40 +02006263void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
6264 struct net_device *netdev, const u8 *buf,
6265 size_t len, gfp_t gfp)
6266{
6267 nl80211_send_mlme_event(rdev, netdev, buf, len,
6268 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
6269}
6270
6271void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
6272 struct net_device *netdev, const u8 *buf,
6273 size_t len, gfp_t gfp)
6274{
6275 nl80211_send_mlme_event(rdev, netdev, buf, len,
6276 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
6277}
6278
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04006279static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
6280 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02006281 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006282{
6283 struct sk_buff *msg;
6284 void *hdr;
6285
Johannes Berge6d6e342009-07-01 21:26:47 +02006286 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006287 if (!msg)
6288 return;
6289
6290 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6291 if (!hdr) {
6292 nlmsg_free(msg);
6293 return;
6294 }
6295
6296 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6297 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6298 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
6299 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6300
6301 if (genlmsg_end(msg, hdr) < 0) {
6302 nlmsg_free(msg);
6303 return;
6304 }
6305
Johannes Berg463d0182009-07-14 00:33:35 +02006306 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6307 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006308 return;
6309
6310 nla_put_failure:
6311 genlmsg_cancel(msg, hdr);
6312 nlmsg_free(msg);
6313}
6314
6315void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006316 struct net_device *netdev, const u8 *addr,
6317 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006318{
6319 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02006320 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006321}
6322
6323void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006324 struct net_device *netdev, const u8 *addr,
6325 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006326{
Johannes Berge6d6e342009-07-01 21:26:47 +02006327 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
6328 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006329}
6330
Samuel Ortizb23aa672009-07-01 21:26:54 +02006331void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
6332 struct net_device *netdev, const u8 *bssid,
6333 const u8 *req_ie, size_t req_ie_len,
6334 const u8 *resp_ie, size_t resp_ie_len,
6335 u16 status, gfp_t gfp)
6336{
6337 struct sk_buff *msg;
6338 void *hdr;
6339
6340 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6341 if (!msg)
6342 return;
6343
6344 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
6345 if (!hdr) {
6346 nlmsg_free(msg);
6347 return;
6348 }
6349
6350 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6351 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6352 if (bssid)
6353 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6354 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
6355 if (req_ie)
6356 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6357 if (resp_ie)
6358 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6359
6360 if (genlmsg_end(msg, hdr) < 0) {
6361 nlmsg_free(msg);
6362 return;
6363 }
6364
Johannes Berg463d0182009-07-14 00:33:35 +02006365 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6366 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006367 return;
6368
6369 nla_put_failure:
6370 genlmsg_cancel(msg, hdr);
6371 nlmsg_free(msg);
6372
6373}
6374
6375void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
6376 struct net_device *netdev, const u8 *bssid,
6377 const u8 *req_ie, size_t req_ie_len,
6378 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
6379{
6380 struct sk_buff *msg;
6381 void *hdr;
6382
6383 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6384 if (!msg)
6385 return;
6386
6387 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
6388 if (!hdr) {
6389 nlmsg_free(msg);
6390 return;
6391 }
6392
6393 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6394 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6395 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6396 if (req_ie)
6397 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6398 if (resp_ie)
6399 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6400
6401 if (genlmsg_end(msg, hdr) < 0) {
6402 nlmsg_free(msg);
6403 return;
6404 }
6405
Johannes Berg463d0182009-07-14 00:33:35 +02006406 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6407 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006408 return;
6409
6410 nla_put_failure:
6411 genlmsg_cancel(msg, hdr);
6412 nlmsg_free(msg);
6413
6414}
6415
6416void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
6417 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02006418 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02006419{
6420 struct sk_buff *msg;
6421 void *hdr;
6422
Johannes Berg667503d2009-07-07 03:56:11 +02006423 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006424 if (!msg)
6425 return;
6426
6427 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
6428 if (!hdr) {
6429 nlmsg_free(msg);
6430 return;
6431 }
6432
6433 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6434 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6435 if (from_ap && reason)
6436 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
6437 if (from_ap)
6438 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
6439 if (ie)
6440 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
6441
6442 if (genlmsg_end(msg, hdr) < 0) {
6443 nlmsg_free(msg);
6444 return;
6445 }
6446
Johannes Berg463d0182009-07-14 00:33:35 +02006447 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6448 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006449 return;
6450
6451 nla_put_failure:
6452 genlmsg_cancel(msg, hdr);
6453 nlmsg_free(msg);
6454
6455}
6456
Johannes Berg04a773a2009-04-19 21:24:32 +02006457void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
6458 struct net_device *netdev, const u8 *bssid,
6459 gfp_t gfp)
6460{
6461 struct sk_buff *msg;
6462 void *hdr;
6463
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006464 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006465 if (!msg)
6466 return;
6467
6468 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
6469 if (!hdr) {
6470 nlmsg_free(msg);
6471 return;
6472 }
6473
6474 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6475 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6476 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6477
6478 if (genlmsg_end(msg, hdr) < 0) {
6479 nlmsg_free(msg);
6480 return;
6481 }
6482
Johannes Berg463d0182009-07-14 00:33:35 +02006483 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6484 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006485 return;
6486
6487 nla_put_failure:
6488 genlmsg_cancel(msg, hdr);
6489 nlmsg_free(msg);
6490}
6491
Javier Cardonac93b5e72011-04-07 15:08:34 -07006492void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
6493 struct net_device *netdev,
6494 const u8 *macaddr, const u8* ie, u8 ie_len,
6495 gfp_t gfp)
6496{
6497 struct sk_buff *msg;
6498 void *hdr;
6499
6500 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6501 if (!msg)
6502 return;
6503
6504 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
6505 if (!hdr) {
6506 nlmsg_free(msg);
6507 return;
6508 }
6509
6510 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6511 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6512 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
6513 if (ie_len && ie)
6514 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
6515
6516 if (genlmsg_end(msg, hdr) < 0) {
6517 nlmsg_free(msg);
6518 return;
6519 }
6520
6521 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6522 nl80211_mlme_mcgrp.id, gfp);
6523 return;
6524
6525 nla_put_failure:
6526 genlmsg_cancel(msg, hdr);
6527 nlmsg_free(msg);
6528}
6529
Jouni Malinena3b8b052009-03-27 21:59:49 +02006530void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
6531 struct net_device *netdev, const u8 *addr,
6532 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02006533 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02006534{
6535 struct sk_buff *msg;
6536 void *hdr;
6537
Johannes Berge6d6e342009-07-01 21:26:47 +02006538 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006539 if (!msg)
6540 return;
6541
6542 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
6543 if (!hdr) {
6544 nlmsg_free(msg);
6545 return;
6546 }
6547
6548 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6549 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6550 if (addr)
6551 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6552 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03006553 if (key_id != -1)
6554 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006555 if (tsc)
6556 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
6557
6558 if (genlmsg_end(msg, hdr) < 0) {
6559 nlmsg_free(msg);
6560 return;
6561 }
6562
Johannes Berg463d0182009-07-14 00:33:35 +02006563 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6564 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006565 return;
6566
6567 nla_put_failure:
6568 genlmsg_cancel(msg, hdr);
6569 nlmsg_free(msg);
6570}
6571
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006572void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
6573 struct ieee80211_channel *channel_before,
6574 struct ieee80211_channel *channel_after)
6575{
6576 struct sk_buff *msg;
6577 void *hdr;
6578 struct nlattr *nl_freq;
6579
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006580 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006581 if (!msg)
6582 return;
6583
6584 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
6585 if (!hdr) {
6586 nlmsg_free(msg);
6587 return;
6588 }
6589
6590 /*
6591 * Since we are applying the beacon hint to a wiphy we know its
6592 * wiphy_idx is valid
6593 */
6594 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
6595
6596 /* Before */
6597 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
6598 if (!nl_freq)
6599 goto nla_put_failure;
6600 if (nl80211_msg_put_channel(msg, channel_before))
6601 goto nla_put_failure;
6602 nla_nest_end(msg, nl_freq);
6603
6604 /* After */
6605 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
6606 if (!nl_freq)
6607 goto nla_put_failure;
6608 if (nl80211_msg_put_channel(msg, channel_after))
6609 goto nla_put_failure;
6610 nla_nest_end(msg, nl_freq);
6611
6612 if (genlmsg_end(msg, hdr) < 0) {
6613 nlmsg_free(msg);
6614 return;
6615 }
6616
Johannes Berg463d0182009-07-14 00:33:35 +02006617 rcu_read_lock();
6618 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
6619 GFP_ATOMIC);
6620 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006621
6622 return;
6623
6624nla_put_failure:
6625 genlmsg_cancel(msg, hdr);
6626 nlmsg_free(msg);
6627}
6628
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006629static void nl80211_send_remain_on_chan_event(
6630 int cmd, struct cfg80211_registered_device *rdev,
6631 struct net_device *netdev, u64 cookie,
6632 struct ieee80211_channel *chan,
6633 enum nl80211_channel_type channel_type,
6634 unsigned int duration, gfp_t gfp)
6635{
6636 struct sk_buff *msg;
6637 void *hdr;
6638
6639 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6640 if (!msg)
6641 return;
6642
6643 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6644 if (!hdr) {
6645 nlmsg_free(msg);
6646 return;
6647 }
6648
6649 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6650 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6651 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
6652 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
6653 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6654
6655 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
6656 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
6657
6658 if (genlmsg_end(msg, hdr) < 0) {
6659 nlmsg_free(msg);
6660 return;
6661 }
6662
6663 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6664 nl80211_mlme_mcgrp.id, gfp);
6665 return;
6666
6667 nla_put_failure:
6668 genlmsg_cancel(msg, hdr);
6669 nlmsg_free(msg);
6670}
6671
6672void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
6673 struct net_device *netdev, u64 cookie,
6674 struct ieee80211_channel *chan,
6675 enum nl80211_channel_type channel_type,
6676 unsigned int duration, gfp_t gfp)
6677{
6678 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
6679 rdev, netdev, cookie, chan,
6680 channel_type, duration, gfp);
6681}
6682
6683void nl80211_send_remain_on_channel_cancel(
6684 struct cfg80211_registered_device *rdev, struct net_device *netdev,
6685 u64 cookie, struct ieee80211_channel *chan,
6686 enum nl80211_channel_type channel_type, gfp_t gfp)
6687{
6688 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6689 rdev, netdev, cookie, chan,
6690 channel_type, 0, gfp);
6691}
6692
Johannes Berg98b62182009-12-23 13:15:44 +01006693void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
6694 struct net_device *dev, const u8 *mac_addr,
6695 struct station_info *sinfo, gfp_t gfp)
6696{
6697 struct sk_buff *msg;
6698
6699 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6700 if (!msg)
6701 return;
6702
6703 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
6704 nlmsg_free(msg);
6705 return;
6706 }
6707
6708 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6709 nl80211_mlme_mcgrp.id, gfp);
6710}
6711
Jouni Malinenec15e682011-03-23 15:29:52 +02006712void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
6713 struct net_device *dev, const u8 *mac_addr,
6714 gfp_t gfp)
6715{
6716 struct sk_buff *msg;
6717 void *hdr;
6718
6719 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6720 if (!msg)
6721 return;
6722
6723 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
6724 if (!hdr) {
6725 nlmsg_free(msg);
6726 return;
6727 }
6728
6729 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
6730 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
6731
6732 if (genlmsg_end(msg, hdr) < 0) {
6733 nlmsg_free(msg);
6734 return;
6735 }
6736
6737 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6738 nl80211_mlme_mcgrp.id, gfp);
6739 return;
6740
6741 nla_put_failure:
6742 genlmsg_cancel(msg, hdr);
6743 nlmsg_free(msg);
6744}
6745
Johannes Berg2e161f72010-08-12 15:38:38 +02006746int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
6747 struct net_device *netdev, u32 nlpid,
6748 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006749{
6750 struct sk_buff *msg;
6751 void *hdr;
6752 int err;
6753
6754 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6755 if (!msg)
6756 return -ENOMEM;
6757
Johannes Berg2e161f72010-08-12 15:38:38 +02006758 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02006759 if (!hdr) {
6760 nlmsg_free(msg);
6761 return -ENOMEM;
6762 }
6763
6764 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6765 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6766 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
6767 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6768
6769 err = genlmsg_end(msg, hdr);
6770 if (err < 0) {
6771 nlmsg_free(msg);
6772 return err;
6773 }
6774
6775 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
6776 if (err < 0)
6777 return err;
6778 return 0;
6779
6780 nla_put_failure:
6781 genlmsg_cancel(msg, hdr);
6782 nlmsg_free(msg);
6783 return -ENOBUFS;
6784}
6785
Johannes Berg2e161f72010-08-12 15:38:38 +02006786void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
6787 struct net_device *netdev, u64 cookie,
6788 const u8 *buf, size_t len, bool ack,
6789 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006790{
6791 struct sk_buff *msg;
6792 void *hdr;
6793
6794 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6795 if (!msg)
6796 return;
6797
Johannes Berg2e161f72010-08-12 15:38:38 +02006798 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02006799 if (!hdr) {
6800 nlmsg_free(msg);
6801 return;
6802 }
6803
6804 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6805 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6806 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6807 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6808 if (ack)
6809 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
6810
6811 if (genlmsg_end(msg, hdr) < 0) {
6812 nlmsg_free(msg);
6813 return;
6814 }
6815
6816 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
6817 return;
6818
6819 nla_put_failure:
6820 genlmsg_cancel(msg, hdr);
6821 nlmsg_free(msg);
6822}
6823
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006824void
6825nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
6826 struct net_device *netdev,
6827 enum nl80211_cqm_rssi_threshold_event rssi_event,
6828 gfp_t gfp)
6829{
6830 struct sk_buff *msg;
6831 struct nlattr *pinfoattr;
6832 void *hdr;
6833
6834 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6835 if (!msg)
6836 return;
6837
6838 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
6839 if (!hdr) {
6840 nlmsg_free(msg);
6841 return;
6842 }
6843
6844 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6845 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6846
6847 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
6848 if (!pinfoattr)
6849 goto nla_put_failure;
6850
6851 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
6852 rssi_event);
6853
6854 nla_nest_end(msg, pinfoattr);
6855
6856 if (genlmsg_end(msg, hdr) < 0) {
6857 nlmsg_free(msg);
6858 return;
6859 }
6860
6861 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6862 nl80211_mlme_mcgrp.id, gfp);
6863 return;
6864
6865 nla_put_failure:
6866 genlmsg_cancel(msg, hdr);
6867 nlmsg_free(msg);
6868}
6869
Johannes Bergc063dbf2010-11-24 08:10:05 +01006870void
6871nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
6872 struct net_device *netdev, const u8 *peer,
6873 u32 num_packets, gfp_t gfp)
6874{
6875 struct sk_buff *msg;
6876 struct nlattr *pinfoattr;
6877 void *hdr;
6878
6879 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6880 if (!msg)
6881 return;
6882
6883 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
6884 if (!hdr) {
6885 nlmsg_free(msg);
6886 return;
6887 }
6888
6889 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6890 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6891 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
6892
6893 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
6894 if (!pinfoattr)
6895 goto nla_put_failure;
6896
6897 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
6898
6899 nla_nest_end(msg, pinfoattr);
6900
6901 if (genlmsg_end(msg, hdr) < 0) {
6902 nlmsg_free(msg);
6903 return;
6904 }
6905
6906 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6907 nl80211_mlme_mcgrp.id, gfp);
6908 return;
6909
6910 nla_put_failure:
6911 genlmsg_cancel(msg, hdr);
6912 nlmsg_free(msg);
6913}
6914
Jouni Malinen026331c2010-02-15 12:53:10 +02006915static int nl80211_netlink_notify(struct notifier_block * nb,
6916 unsigned long state,
6917 void *_notify)
6918{
6919 struct netlink_notify *notify = _notify;
6920 struct cfg80211_registered_device *rdev;
6921 struct wireless_dev *wdev;
6922
6923 if (state != NETLINK_URELEASE)
6924 return NOTIFY_DONE;
6925
6926 rcu_read_lock();
6927
6928 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
6929 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02006930 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Jouni Malinen026331c2010-02-15 12:53:10 +02006931
6932 rcu_read_unlock();
6933
6934 return NOTIFY_DONE;
6935}
6936
6937static struct notifier_block nl80211_netlink_notifier = {
6938 .notifier_call = nl80211_netlink_notify,
6939};
6940
Johannes Berg55682962007-09-20 13:09:35 -04006941/* initialisation/exit functions */
6942
6943int nl80211_init(void)
6944{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006945 int err;
Johannes Berg55682962007-09-20 13:09:35 -04006946
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00006947 err = genl_register_family_with_ops(&nl80211_fam,
6948 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04006949 if (err)
6950 return err;
6951
Johannes Berg55682962007-09-20 13:09:35 -04006952 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
6953 if (err)
6954 goto err_out;
6955
Johannes Berg2a519312009-02-10 21:25:55 +01006956 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
6957 if (err)
6958 goto err_out;
6959
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006960 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
6961 if (err)
6962 goto err_out;
6963
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006964 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
6965 if (err)
6966 goto err_out;
6967
Johannes Bergaff89a92009-07-01 21:26:51 +02006968#ifdef CONFIG_NL80211_TESTMODE
6969 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
6970 if (err)
6971 goto err_out;
6972#endif
6973
Jouni Malinen026331c2010-02-15 12:53:10 +02006974 err = netlink_register_notifier(&nl80211_netlink_notifier);
6975 if (err)
6976 goto err_out;
6977
Johannes Berg55682962007-09-20 13:09:35 -04006978 return 0;
6979 err_out:
6980 genl_unregister_family(&nl80211_fam);
6981 return err;
6982}
6983
6984void nl80211_exit(void)
6985{
Jouni Malinen026331c2010-02-15 12:53:10 +02006986 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04006987 genl_unregister_family(&nl80211_fam);
6988}