blob: fbb63d3ddc7869c9eafdb60f127da6fdb2947746 [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
Johannes Berg4c476992010-10-04 21:36:35 +020026static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
27 struct genl_info *info);
28static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
29 struct genl_info *info);
30
Johannes Berg55682962007-09-20 13:09:35 -040031/* the netlink family */
32static struct genl_family nl80211_fam = {
33 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
34 .name = "nl80211", /* have users key off the name instead */
35 .hdrsize = 0, /* no private header */
36 .version = 1, /* no particular meaning now */
37 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020038 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020039 .pre_doit = nl80211_pre_doit,
40 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040041};
42
Johannes Berg79c97e92009-07-07 03:56:12 +020043/* internal helper: get rdev and dev */
Johannes Berg463d0182009-07-14 00:33:35 +020044static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020045 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040046 struct net_device **dev)
47{
Johannes Berg463d0182009-07-14 00:33:35 +020048 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040049 int ifindex;
50
Johannes Bergbba95fe2008-07-29 13:22:51 +020051 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040052 return -EINVAL;
53
Johannes Bergbba95fe2008-07-29 13:22:51 +020054 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020055 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040056 if (!*dev)
57 return -ENODEV;
58
Johannes Berg463d0182009-07-14 00:33:35 +020059 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020060 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040061 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020062 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040063 }
64
65 return 0;
66}
67
68/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000069static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040070 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
71 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070072 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020073 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020074 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053075 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020076 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
77 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
78 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
79 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010080 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040081
82 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
83 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
84 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010085
86 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020087 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010088
Johannes Bergb9454e82009-07-08 13:29:08 +020089 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010090 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
91 .len = WLAN_MAX_KEY_LEN },
92 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
93 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
94 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen9f26a952009-05-15 12:38:32 +030095 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
Johannes Berge31b8212010-10-05 19:39:30 +020096 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +010097
98 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
99 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
100 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
101 .len = IEEE80211_MAX_DATA_LEN },
102 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
103 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100104 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
105 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
106 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
107 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
108 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100109 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100110 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200111 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100112 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
113 .len = IEEE80211_MAX_MESH_ID_LEN },
114 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300115
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700116 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
117 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
118
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300119 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
120 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
121 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200122 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
123 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100124 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300125
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800126 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700127 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700128
Jouni Malinen36aedc92008-08-25 11:58:58 +0300129 [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY,
130 .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200131
132 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
133 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
134 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100135 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
136 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200137
138 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
139 .len = IEEE80211_MAX_SSID_LEN },
140 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
141 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200142 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300143 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382ce2009-05-06 22:09:37 +0300144 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300145 [NL80211_ATTR_STA_FLAGS2] = {
146 .len = sizeof(struct nl80211_sta_flag_update),
147 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300148 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300149 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
150 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200151 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
152 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
153 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200154 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100155 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100156 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
157 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100158 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
159 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200160 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200161 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
162 .len = IEEE80211_MAX_DATA_LEN },
163 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200164 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200165 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300166 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200167 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300168 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
169 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200170 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900171 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
172 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100173 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100174 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100175 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200176 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700177 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300178 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200179 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200180 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300181 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Johannes Berg55682962007-09-20 13:09:35 -0400182};
183
Johannes Berge31b8212010-10-05 19:39:30 +0200184/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000185static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200186 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200187 [NL80211_KEY_IDX] = { .type = NLA_U8 },
188 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
189 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 8 },
190 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
191 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200192 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100193 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
194};
195
196/* policy for the key default flags */
197static const struct nla_policy
198nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
199 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
200 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200201};
202
Johannes Bergff1b6e62011-05-04 15:37:28 +0200203/* policy for WoWLAN attributes */
204static const struct nla_policy
205nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
206 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
207 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
208 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
209 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200210 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
211 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
212 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
213 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200214};
215
Johannes Berge5497d72011-07-05 16:35:40 +0200216/* policy for GTK rekey offload attributes */
217static const struct nla_policy
218nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
219 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
220 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
221 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
222};
223
Holger Schuriga0438972009-11-11 11:30:02 +0100224/* ifidx get helper */
225static int nl80211_get_ifidx(struct netlink_callback *cb)
226{
227 int res;
228
229 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
230 nl80211_fam.attrbuf, nl80211_fam.maxattr,
231 nl80211_policy);
232 if (res)
233 return res;
234
235 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
236 return -EINVAL;
237
238 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
239 if (!res)
240 return -EINVAL;
241 return res;
242}
243
Johannes Berg67748892010-10-04 21:14:06 +0200244static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
245 struct netlink_callback *cb,
246 struct cfg80211_registered_device **rdev,
247 struct net_device **dev)
248{
249 int ifidx = cb->args[0];
250 int err;
251
252 if (!ifidx)
253 ifidx = nl80211_get_ifidx(cb);
254 if (ifidx < 0)
255 return ifidx;
256
257 cb->args[0] = ifidx;
258
259 rtnl_lock();
260
261 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
262 if (!*dev) {
263 err = -ENODEV;
264 goto out_rtnl;
265 }
266
267 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100268 if (IS_ERR(*rdev)) {
269 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200270 goto out_rtnl;
271 }
272
273 return 0;
274 out_rtnl:
275 rtnl_unlock();
276 return err;
277}
278
279static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
280{
281 cfg80211_unlock_rdev(rdev);
282 rtnl_unlock();
283}
284
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100285/* IE validation */
286static bool is_valid_ie_attr(const struct nlattr *attr)
287{
288 const u8 *pos;
289 int len;
290
291 if (!attr)
292 return true;
293
294 pos = nla_data(attr);
295 len = nla_len(attr);
296
297 while (len) {
298 u8 elemlen;
299
300 if (len < 2)
301 return false;
302 len -= 2;
303
304 elemlen = pos[1];
305 if (elemlen > len)
306 return false;
307
308 len -= elemlen;
309 pos += 2 + elemlen;
310 }
311
312 return true;
313}
314
Johannes Berg55682962007-09-20 13:09:35 -0400315/* message building helper */
316static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
317 int flags, u8 cmd)
318{
319 /* since there is no private header just add the generic one */
320 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
321}
322
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400323static int nl80211_msg_put_channel(struct sk_buff *msg,
324 struct ieee80211_channel *chan)
325{
326 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
327 chan->center_freq);
328
329 if (chan->flags & IEEE80211_CHAN_DISABLED)
330 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
331 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
332 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
333 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
334 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
335 if (chan->flags & IEEE80211_CHAN_RADAR)
336 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
337
338 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
339 DBM_TO_MBM(chan->max_power));
340
341 return 0;
342
343 nla_put_failure:
344 return -ENOBUFS;
345}
346
Johannes Berg55682962007-09-20 13:09:35 -0400347/* netlink command implementations */
348
Johannes Bergb9454e82009-07-08 13:29:08 +0200349struct key_parse {
350 struct key_params p;
351 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200352 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200353 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100354 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200355};
356
357static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
358{
359 struct nlattr *tb[NL80211_KEY_MAX + 1];
360 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
361 nl80211_key_policy);
362 if (err)
363 return err;
364
365 k->def = !!tb[NL80211_KEY_DEFAULT];
366 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
367
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100368 if (k->def) {
369 k->def_uni = true;
370 k->def_multi = true;
371 }
372 if (k->defmgmt)
373 k->def_multi = true;
374
Johannes Bergb9454e82009-07-08 13:29:08 +0200375 if (tb[NL80211_KEY_IDX])
376 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
377
378 if (tb[NL80211_KEY_DATA]) {
379 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
380 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
381 }
382
383 if (tb[NL80211_KEY_SEQ]) {
384 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
385 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
386 }
387
388 if (tb[NL80211_KEY_CIPHER])
389 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
390
Johannes Berge31b8212010-10-05 19:39:30 +0200391 if (tb[NL80211_KEY_TYPE]) {
392 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
393 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
394 return -EINVAL;
395 }
396
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100397 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
398 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
399 int err = nla_parse_nested(kdt,
400 NUM_NL80211_KEY_DEFAULT_TYPES - 1,
401 tb[NL80211_KEY_DEFAULT_TYPES],
402 nl80211_key_default_policy);
403 if (err)
404 return err;
405
406 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
407 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
408 }
409
Johannes Bergb9454e82009-07-08 13:29:08 +0200410 return 0;
411}
412
413static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
414{
415 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
416 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
417 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
418 }
419
420 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
421 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
422 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
423 }
424
425 if (info->attrs[NL80211_ATTR_KEY_IDX])
426 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
427
428 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
429 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
430
431 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
432 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
433
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100434 if (k->def) {
435 k->def_uni = true;
436 k->def_multi = true;
437 }
438 if (k->defmgmt)
439 k->def_multi = true;
440
Johannes Berge31b8212010-10-05 19:39:30 +0200441 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
442 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
443 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
444 return -EINVAL;
445 }
446
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100447 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
448 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
449 int err = nla_parse_nested(
450 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
451 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
452 nl80211_key_default_policy);
453 if (err)
454 return err;
455
456 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
457 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
458 }
459
Johannes Bergb9454e82009-07-08 13:29:08 +0200460 return 0;
461}
462
463static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
464{
465 int err;
466
467 memset(k, 0, sizeof(*k));
468 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200469 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200470
471 if (info->attrs[NL80211_ATTR_KEY])
472 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
473 else
474 err = nl80211_parse_key_old(info, k);
475
476 if (err)
477 return err;
478
479 if (k->def && k->defmgmt)
480 return -EINVAL;
481
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100482 if (k->defmgmt) {
483 if (k->def_uni || !k->def_multi)
484 return -EINVAL;
485 }
486
Johannes Bergb9454e82009-07-08 13:29:08 +0200487 if (k->idx != -1) {
488 if (k->defmgmt) {
489 if (k->idx < 4 || k->idx > 5)
490 return -EINVAL;
491 } else if (k->def) {
492 if (k->idx < 0 || k->idx > 3)
493 return -EINVAL;
494 } else {
495 if (k->idx < 0 || k->idx > 5)
496 return -EINVAL;
497 }
498 }
499
500 return 0;
501}
502
Johannes Bergfffd0932009-07-08 14:22:54 +0200503static struct cfg80211_cached_keys *
504nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
505 struct nlattr *keys)
506{
507 struct key_parse parse;
508 struct nlattr *key;
509 struct cfg80211_cached_keys *result;
510 int rem, err, def = 0;
511
512 result = kzalloc(sizeof(*result), GFP_KERNEL);
513 if (!result)
514 return ERR_PTR(-ENOMEM);
515
516 result->def = -1;
517 result->defmgmt = -1;
518
519 nla_for_each_nested(key, keys, rem) {
520 memset(&parse, 0, sizeof(parse));
521 parse.idx = -1;
522
523 err = nl80211_parse_key_new(key, &parse);
524 if (err)
525 goto error;
526 err = -EINVAL;
527 if (!parse.p.key)
528 goto error;
529 if (parse.idx < 0 || parse.idx > 4)
530 goto error;
531 if (parse.def) {
532 if (def)
533 goto error;
534 def = 1;
535 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100536 if (!parse.def_uni || !parse.def_multi)
537 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200538 } else if (parse.defmgmt)
539 goto error;
540 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200541 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200542 if (err)
543 goto error;
544 result->params[parse.idx].cipher = parse.p.cipher;
545 result->params[parse.idx].key_len = parse.p.key_len;
546 result->params[parse.idx].key = result->data[parse.idx];
547 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
548 }
549
550 return result;
551 error:
552 kfree(result);
553 return ERR_PTR(err);
554}
555
556static int nl80211_key_allowed(struct wireless_dev *wdev)
557{
558 ASSERT_WDEV_LOCK(wdev);
559
Johannes Bergfffd0932009-07-08 14:22:54 +0200560 switch (wdev->iftype) {
561 case NL80211_IFTYPE_AP:
562 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200563 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700564 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200565 break;
566 case NL80211_IFTYPE_ADHOC:
567 if (!wdev->current_bss)
568 return -ENOLINK;
569 break;
570 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200571 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200572 if (wdev->sme_state != CFG80211_SME_CONNECTED)
573 return -ENOLINK;
574 break;
575 default:
576 return -EINVAL;
577 }
578
579 return 0;
580}
581
Johannes Berg7527a782011-05-13 10:58:57 +0200582static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
583{
584 struct nlattr *nl_modes = nla_nest_start(msg, attr);
585 int i;
586
587 if (!nl_modes)
588 goto nla_put_failure;
589
590 i = 0;
591 while (ifmodes) {
592 if (ifmodes & 1)
593 NLA_PUT_FLAG(msg, i);
594 ifmodes >>= 1;
595 i++;
596 }
597
598 nla_nest_end(msg, nl_modes);
599 return 0;
600
601nla_put_failure:
602 return -ENOBUFS;
603}
604
605static int nl80211_put_iface_combinations(struct wiphy *wiphy,
606 struct sk_buff *msg)
607{
608 struct nlattr *nl_combis;
609 int i, j;
610
611 nl_combis = nla_nest_start(msg,
612 NL80211_ATTR_INTERFACE_COMBINATIONS);
613 if (!nl_combis)
614 goto nla_put_failure;
615
616 for (i = 0; i < wiphy->n_iface_combinations; i++) {
617 const struct ieee80211_iface_combination *c;
618 struct nlattr *nl_combi, *nl_limits;
619
620 c = &wiphy->iface_combinations[i];
621
622 nl_combi = nla_nest_start(msg, i + 1);
623 if (!nl_combi)
624 goto nla_put_failure;
625
626 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
627 if (!nl_limits)
628 goto nla_put_failure;
629
630 for (j = 0; j < c->n_limits; j++) {
631 struct nlattr *nl_limit;
632
633 nl_limit = nla_nest_start(msg, j + 1);
634 if (!nl_limit)
635 goto nla_put_failure;
636 NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
637 c->limits[j].max);
638 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
639 c->limits[j].types))
640 goto nla_put_failure;
641 nla_nest_end(msg, nl_limit);
642 }
643
644 nla_nest_end(msg, nl_limits);
645
646 if (c->beacon_int_infra_match)
647 NLA_PUT_FLAG(msg,
648 NL80211_IFACE_COMB_STA_AP_BI_MATCH);
649 NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
650 c->num_different_channels);
651 NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
652 c->max_interfaces);
653
654 nla_nest_end(msg, nl_combi);
655 }
656
657 nla_nest_end(msg, nl_combis);
658
659 return 0;
660nla_put_failure:
661 return -ENOBUFS;
662}
663
Johannes Berg55682962007-09-20 13:09:35 -0400664static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
665 struct cfg80211_registered_device *dev)
666{
667 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100668 struct nlattr *nl_bands, *nl_band;
669 struct nlattr *nl_freqs, *nl_freq;
670 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100671 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100672 enum ieee80211_band band;
673 struct ieee80211_channel *chan;
674 struct ieee80211_rate *rate;
675 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200676 const struct ieee80211_txrx_stypes *mgmt_stypes =
677 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400678
679 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
680 if (!hdr)
681 return -1;
682
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500683 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400684 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200685
Johannes Bergf5ea9122009-08-07 16:17:38 +0200686 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
687 cfg80211_rdev_list_generation);
688
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200689 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
690 dev->wiphy.retry_short);
691 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
692 dev->wiphy.retry_long);
693 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
694 dev->wiphy.frag_threshold);
695 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
696 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100697 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
698 dev->wiphy.coverage_class);
Johannes Berg2a519312009-02-10 21:25:55 +0100699 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
700 dev->wiphy.max_scan_ssids);
Luciano Coelho93b6aa62011-07-13 14:57:28 +0300701 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
702 dev->wiphy.max_sched_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200703 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
704 dev->wiphy.max_scan_ie_len);
Luciano Coelho5a865ba2011-07-13 14:57:29 +0300705 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
706 dev->wiphy.max_sched_scan_ie_len);
Johannes Bergee688b002008-01-24 19:38:39 +0100707
Johannes Berge31b8212010-10-05 19:39:30 +0200708 if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
709 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
Javier Cardona15d5dda2011-04-07 15:08:28 -0700710 if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
711 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
Johannes Berge31b8212010-10-05 19:39:30 +0200712
Johannes Berg25e47c182009-04-02 20:14:06 +0200713 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
714 sizeof(u32) * dev->wiphy.n_cipher_suites,
715 dev->wiphy.cipher_suites);
716
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100717 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
718 dev->wiphy.max_num_pmkids);
719
Johannes Bergc0692b82010-08-27 14:26:53 +0300720 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
721 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
722
Bruno Randolf39fd5de2010-12-16 11:30:28 +0900723 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
724 dev->wiphy.available_antennas_tx);
725 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
726 dev->wiphy.available_antennas_rx);
727
Bruno Randolf7f531e02010-12-16 11:30:22 +0900728 if ((dev->wiphy.available_antennas_tx ||
729 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900730 u32 tx_ant = 0, rx_ant = 0;
731 int res;
732 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
733 if (!res) {
734 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
735 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
736 }
737 }
738
Johannes Berg7527a782011-05-13 10:58:57 +0200739 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
740 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700741 goto nla_put_failure;
742
Johannes Bergee688b002008-01-24 19:38:39 +0100743 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
744 if (!nl_bands)
745 goto nla_put_failure;
746
747 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
748 if (!dev->wiphy.bands[band])
749 continue;
750
751 nl_band = nla_nest_start(msg, band);
752 if (!nl_band)
753 goto nla_put_failure;
754
Johannes Bergd51626d2008-10-09 12:20:13 +0200755 /* add HT info */
756 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
757 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
758 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
759 &dev->wiphy.bands[band]->ht_cap.mcs);
760 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
761 dev->wiphy.bands[band]->ht_cap.cap);
762 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
763 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
764 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
765 dev->wiphy.bands[band]->ht_cap.ampdu_density);
766 }
767
Johannes Bergee688b002008-01-24 19:38:39 +0100768 /* add frequencies */
769 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
770 if (!nl_freqs)
771 goto nla_put_failure;
772
773 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
774 nl_freq = nla_nest_start(msg, i);
775 if (!nl_freq)
776 goto nla_put_failure;
777
778 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100779
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400780 if (nl80211_msg_put_channel(msg, chan))
781 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200782
Johannes Bergee688b002008-01-24 19:38:39 +0100783 nla_nest_end(msg, nl_freq);
784 }
785
786 nla_nest_end(msg, nl_freqs);
787
788 /* add bitrates */
789 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
790 if (!nl_rates)
791 goto nla_put_failure;
792
793 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
794 nl_rate = nla_nest_start(msg, i);
795 if (!nl_rate)
796 goto nla_put_failure;
797
798 rate = &dev->wiphy.bands[band]->bitrates[i];
799 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
800 rate->bitrate);
801 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
802 NLA_PUT_FLAG(msg,
803 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
804
805 nla_nest_end(msg, nl_rate);
806 }
807
808 nla_nest_end(msg, nl_rates);
809
810 nla_nest_end(msg, nl_band);
811 }
812 nla_nest_end(msg, nl_bands);
813
Johannes Berg8fdc6212009-03-14 09:34:01 +0100814 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
815 if (!nl_cmds)
816 goto nla_put_failure;
817
818 i = 0;
819#define CMD(op, n) \
820 do { \
821 if (dev->ops->op) { \
822 i++; \
823 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
824 } \
825 } while (0)
826
827 CMD(add_virtual_intf, NEW_INTERFACE);
828 CMD(change_virtual_intf, SET_INTERFACE);
829 CMD(add_key, NEW_KEY);
830 CMD(add_beacon, NEW_BEACON);
831 CMD(add_station, NEW_STATION);
832 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800833 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100834 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200835 CMD(auth, AUTHENTICATE);
836 CMD(assoc, ASSOCIATE);
837 CMD(deauth, DEAUTHENTICATE);
838 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200839 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100840 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100841 CMD(set_pmksa, SET_PMKSA);
842 CMD(del_pmksa, DEL_PMKSA);
843 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100844 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200845 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200846 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100847 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100848 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200849 i++;
850 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
851 }
Johannes Bergf444de02010-05-05 15:25:02 +0200852 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400853 CMD(set_wds_peer, SET_WDS_PEER);
Luciano Coelho807f8a82011-05-11 17:09:35 +0300854 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
855 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100856
857#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200858
Johannes Berg6829c8782009-07-02 09:13:27 +0200859 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200860 i++;
861 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
862 }
863
Johannes Berg6829c8782009-07-02 09:13:27 +0200864 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200865 i++;
866 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
867 }
868
Johannes Berg8fdc6212009-03-14 09:34:01 +0100869 nla_nest_end(msg, nl_cmds);
870
Johannes Berga2939112010-12-14 17:54:28 +0100871 if (dev->ops->remain_on_channel)
872 NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
873 dev->wiphy.max_remain_on_channel_duration);
874
Jouni Malinend2da5872011-08-08 12:10:30 +0300875 if (dev->ops->mgmt_tx_cancel_wait)
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100876 NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
877
Johannes Berg2e161f72010-08-12 15:38:38 +0200878 if (mgmt_stypes) {
879 u16 stypes;
880 struct nlattr *nl_ftypes, *nl_ifs;
881 enum nl80211_iftype ift;
882
883 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
884 if (!nl_ifs)
885 goto nla_put_failure;
886
887 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
888 nl_ftypes = nla_nest_start(msg, ift);
889 if (!nl_ftypes)
890 goto nla_put_failure;
891 i = 0;
892 stypes = mgmt_stypes[ift].tx;
893 while (stypes) {
894 if (stypes & 1)
895 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
896 (i << 4) | IEEE80211_FTYPE_MGMT);
897 stypes >>= 1;
898 i++;
899 }
900 nla_nest_end(msg, nl_ftypes);
901 }
902
Johannes Berg74b70a42010-08-24 12:15:53 +0200903 nla_nest_end(msg, nl_ifs);
904
Johannes Berg2e161f72010-08-12 15:38:38 +0200905 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
906 if (!nl_ifs)
907 goto nla_put_failure;
908
909 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
910 nl_ftypes = nla_nest_start(msg, ift);
911 if (!nl_ftypes)
912 goto nla_put_failure;
913 i = 0;
914 stypes = mgmt_stypes[ift].rx;
915 while (stypes) {
916 if (stypes & 1)
917 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
918 (i << 4) | IEEE80211_FTYPE_MGMT);
919 stypes >>= 1;
920 i++;
921 }
922 nla_nest_end(msg, nl_ftypes);
923 }
924 nla_nest_end(msg, nl_ifs);
925 }
926
Johannes Bergff1b6e62011-05-04 15:37:28 +0200927 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
928 struct nlattr *nl_wowlan;
929
930 nl_wowlan = nla_nest_start(msg,
931 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
932 if (!nl_wowlan)
933 goto nla_put_failure;
934
935 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
936 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
937 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
938 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
939 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
940 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +0200941 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
942 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
943 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
944 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
945 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
946 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
947 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
948 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
949 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
950 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +0200951 if (dev->wiphy.wowlan.n_patterns) {
952 struct nl80211_wowlan_pattern_support pat = {
953 .max_patterns = dev->wiphy.wowlan.n_patterns,
954 .min_pattern_len =
955 dev->wiphy.wowlan.pattern_min_len,
956 .max_pattern_len =
957 dev->wiphy.wowlan.pattern_max_len,
958 };
959 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
960 sizeof(pat), &pat);
961 }
962
963 nla_nest_end(msg, nl_wowlan);
964 }
965
Johannes Berg7527a782011-05-13 10:58:57 +0200966 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
967 dev->wiphy.software_iftypes))
968 goto nla_put_failure;
969
970 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
971 goto nla_put_failure;
972
Johannes Berg55682962007-09-20 13:09:35 -0400973 return genlmsg_end(msg, hdr);
974
975 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -0700976 genlmsg_cancel(msg, hdr);
977 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -0400978}
979
980static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
981{
982 int idx = 0;
983 int start = cb->args[0];
984 struct cfg80211_registered_device *dev;
985
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500986 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +0200987 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +0200988 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
989 continue;
Julius Volzb4637272008-07-08 14:02:19 +0200990 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -0400991 continue;
992 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
993 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +0200994 dev) < 0) {
995 idx--;
Johannes Berg55682962007-09-20 13:09:35 -0400996 break;
Julius Volzb4637272008-07-08 14:02:19 +0200997 }
Johannes Berg55682962007-09-20 13:09:35 -0400998 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -0500999 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001000
1001 cb->args[0] = idx;
1002
1003 return skb->len;
1004}
1005
1006static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1007{
1008 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001009 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001010
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001011 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001012 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001013 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001014
Johannes Berg4c476992010-10-04 21:36:35 +02001015 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1016 nlmsg_free(msg);
1017 return -ENOBUFS;
1018 }
Johannes Berg55682962007-09-20 13:09:35 -04001019
Johannes Berg134e6372009-07-10 09:51:34 +00001020 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001021}
1022
Jouni Malinen31888482008-10-30 16:59:24 +02001023static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1024 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1025 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1026 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1027 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1028 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1029};
1030
1031static int parse_txq_params(struct nlattr *tb[],
1032 struct ieee80211_txq_params *txq_params)
1033{
1034 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
1035 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1036 !tb[NL80211_TXQ_ATTR_AIFS])
1037 return -EINVAL;
1038
1039 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
1040 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1041 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1042 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1043 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1044
1045 return 0;
1046}
1047
Johannes Bergf444de02010-05-05 15:25:02 +02001048static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1049{
1050 /*
1051 * You can only set the channel explicitly for AP, mesh
1052 * and WDS type interfaces; all others have their channel
1053 * managed via their respective "establish a connection"
1054 * command (connect, join, ...)
1055 *
1056 * Monitors are special as they are normally slaved to
1057 * whatever else is going on, so they behave as though
1058 * you tried setting the wiphy channel itself.
1059 */
1060 return !wdev ||
1061 wdev->iftype == NL80211_IFTYPE_AP ||
1062 wdev->iftype == NL80211_IFTYPE_WDS ||
1063 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001064 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1065 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001066}
1067
1068static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1069 struct wireless_dev *wdev,
1070 struct genl_info *info)
1071{
1072 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1073 u32 freq;
1074 int result;
1075
1076 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1077 return -EINVAL;
1078
1079 if (!nl80211_can_set_dev_channel(wdev))
1080 return -EOPNOTSUPP;
1081
1082 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1083 channel_type = nla_get_u32(info->attrs[
1084 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1085 if (channel_type != NL80211_CHAN_NO_HT &&
1086 channel_type != NL80211_CHAN_HT20 &&
1087 channel_type != NL80211_CHAN_HT40PLUS &&
1088 channel_type != NL80211_CHAN_HT40MINUS)
1089 return -EINVAL;
1090 }
1091
1092 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1093
1094 mutex_lock(&rdev->devlist_mtx);
1095 if (wdev) {
1096 wdev_lock(wdev);
1097 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
1098 wdev_unlock(wdev);
1099 } else {
1100 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
1101 }
1102 mutex_unlock(&rdev->devlist_mtx);
1103
1104 return result;
1105}
1106
1107static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1108{
Johannes Berg4c476992010-10-04 21:36:35 +02001109 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1110 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001111
Johannes Berg4c476992010-10-04 21:36:35 +02001112 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001113}
1114
Bill Jordane8347eb2010-10-01 13:54:28 -04001115static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1116{
Johannes Berg43b19952010-10-07 13:10:30 +02001117 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1118 struct net_device *dev = info->user_ptr[1];
1119 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001120 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001121
1122 if (!info->attrs[NL80211_ATTR_MAC])
1123 return -EINVAL;
1124
Johannes Berg43b19952010-10-07 13:10:30 +02001125 if (netif_running(dev))
1126 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001127
Johannes Berg43b19952010-10-07 13:10:30 +02001128 if (!rdev->ops->set_wds_peer)
1129 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001130
Johannes Berg43b19952010-10-07 13:10:30 +02001131 if (wdev->iftype != NL80211_IFTYPE_WDS)
1132 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001133
1134 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001135 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001136}
1137
1138
Johannes Berg55682962007-09-20 13:09:35 -04001139static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1140{
1141 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001142 struct net_device *netdev = NULL;
1143 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001144 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001145 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001146 u32 changed;
1147 u8 retry_short = 0, retry_long = 0;
1148 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001149 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001150
Johannes Bergf444de02010-05-05 15:25:02 +02001151 /*
1152 * Try to find the wiphy and netdev. Normally this
1153 * function shouldn't need the netdev, but this is
1154 * done for backward compatibility -- previously
1155 * setting the channel was done per wiphy, but now
1156 * it is per netdev. Previous userland like hostapd
1157 * also passed a netdev to set_wiphy, so that it is
1158 * possible to let that go to the right netdev!
1159 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001160 mutex_lock(&cfg80211_mutex);
1161
Johannes Bergf444de02010-05-05 15:25:02 +02001162 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1163 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1164
1165 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1166 if (netdev && netdev->ieee80211_ptr) {
1167 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1168 mutex_lock(&rdev->mtx);
1169 } else
1170 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001171 }
1172
Johannes Bergf444de02010-05-05 15:25:02 +02001173 if (!netdev) {
1174 rdev = __cfg80211_rdev_from_info(info);
1175 if (IS_ERR(rdev)) {
1176 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001177 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001178 }
1179 wdev = NULL;
1180 netdev = NULL;
1181 result = 0;
1182
1183 mutex_lock(&rdev->mtx);
1184 } else if (netif_running(netdev) &&
1185 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
1186 wdev = netdev->ieee80211_ptr;
1187 else
1188 wdev = NULL;
1189
1190 /*
1191 * end workaround code, by now the rdev is available
1192 * and locked, and wdev may or may not be NULL.
1193 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001194
1195 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001196 result = cfg80211_dev_rename(
1197 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001198
1199 mutex_unlock(&cfg80211_mutex);
1200
1201 if (result)
1202 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001203
Jouni Malinen31888482008-10-30 16:59:24 +02001204 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1205 struct ieee80211_txq_params txq_params;
1206 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1207
1208 if (!rdev->ops->set_txq_params) {
1209 result = -EOPNOTSUPP;
1210 goto bad_res;
1211 }
1212
1213 nla_for_each_nested(nl_txq_params,
1214 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1215 rem_txq_params) {
1216 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1217 nla_data(nl_txq_params),
1218 nla_len(nl_txq_params),
1219 txq_params_policy);
1220 result = parse_txq_params(tb, &txq_params);
1221 if (result)
1222 goto bad_res;
1223
1224 result = rdev->ops->set_txq_params(&rdev->wiphy,
1225 &txq_params);
1226 if (result)
1227 goto bad_res;
1228 }
1229 }
1230
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001231 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001232 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001233 if (result)
1234 goto bad_res;
1235 }
1236
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001237 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1238 enum nl80211_tx_power_setting type;
1239 int idx, mbm = 0;
1240
1241 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001242 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001243 goto bad_res;
1244 }
1245
1246 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1247 type = nla_get_u32(info->attrs[idx]);
1248
1249 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1250 (type != NL80211_TX_POWER_AUTOMATIC)) {
1251 result = -EINVAL;
1252 goto bad_res;
1253 }
1254
1255 if (type != NL80211_TX_POWER_AUTOMATIC) {
1256 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1257 mbm = nla_get_u32(info->attrs[idx]);
1258 }
1259
1260 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1261 if (result)
1262 goto bad_res;
1263 }
1264
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001265 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1266 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1267 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001268 if ((!rdev->wiphy.available_antennas_tx &&
1269 !rdev->wiphy.available_antennas_rx) ||
1270 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001271 result = -EOPNOTSUPP;
1272 goto bad_res;
1273 }
1274
1275 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1276 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1277
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001278 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001279 * available antenna masks, except for the "all" mask */
1280 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1281 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001282 result = -EINVAL;
1283 goto bad_res;
1284 }
1285
Bruno Randolf7f531e02010-12-16 11:30:22 +09001286 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1287 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001288
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001289 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1290 if (result)
1291 goto bad_res;
1292 }
1293
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001294 changed = 0;
1295
1296 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1297 retry_short = nla_get_u8(
1298 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1299 if (retry_short == 0) {
1300 result = -EINVAL;
1301 goto bad_res;
1302 }
1303 changed |= WIPHY_PARAM_RETRY_SHORT;
1304 }
1305
1306 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1307 retry_long = nla_get_u8(
1308 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1309 if (retry_long == 0) {
1310 result = -EINVAL;
1311 goto bad_res;
1312 }
1313 changed |= WIPHY_PARAM_RETRY_LONG;
1314 }
1315
1316 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1317 frag_threshold = nla_get_u32(
1318 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1319 if (frag_threshold < 256) {
1320 result = -EINVAL;
1321 goto bad_res;
1322 }
1323 if (frag_threshold != (u32) -1) {
1324 /*
1325 * Fragments (apart from the last one) are required to
1326 * have even length. Make the fragmentation code
1327 * simpler by stripping LSB should someone try to use
1328 * odd threshold value.
1329 */
1330 frag_threshold &= ~0x1;
1331 }
1332 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1333 }
1334
1335 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1336 rts_threshold = nla_get_u32(
1337 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1338 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1339 }
1340
Lukáš Turek81077e82009-12-21 22:50:47 +01001341 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1342 coverage_class = nla_get_u8(
1343 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1344 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1345 }
1346
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001347 if (changed) {
1348 u8 old_retry_short, old_retry_long;
1349 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001350 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001351
1352 if (!rdev->ops->set_wiphy_params) {
1353 result = -EOPNOTSUPP;
1354 goto bad_res;
1355 }
1356
1357 old_retry_short = rdev->wiphy.retry_short;
1358 old_retry_long = rdev->wiphy.retry_long;
1359 old_frag_threshold = rdev->wiphy.frag_threshold;
1360 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001361 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001362
1363 if (changed & WIPHY_PARAM_RETRY_SHORT)
1364 rdev->wiphy.retry_short = retry_short;
1365 if (changed & WIPHY_PARAM_RETRY_LONG)
1366 rdev->wiphy.retry_long = retry_long;
1367 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1368 rdev->wiphy.frag_threshold = frag_threshold;
1369 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1370 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001371 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1372 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001373
1374 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1375 if (result) {
1376 rdev->wiphy.retry_short = old_retry_short;
1377 rdev->wiphy.retry_long = old_retry_long;
1378 rdev->wiphy.frag_threshold = old_frag_threshold;
1379 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001380 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001381 }
1382 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001383
Johannes Berg306d6112008-12-08 12:39:04 +01001384 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001385 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001386 if (netdev)
1387 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001388 return result;
1389}
1390
1391
1392static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001393 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001394 struct net_device *dev)
1395{
1396 void *hdr;
1397
1398 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1399 if (!hdr)
1400 return -1;
1401
1402 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001403 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001404 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001405 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001406
1407 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1408 rdev->devlist_generation ^
1409 (cfg80211_rdev_list_generation << 2));
1410
Johannes Berg55682962007-09-20 13:09:35 -04001411 return genlmsg_end(msg, hdr);
1412
1413 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001414 genlmsg_cancel(msg, hdr);
1415 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001416}
1417
1418static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1419{
1420 int wp_idx = 0;
1421 int if_idx = 0;
1422 int wp_start = cb->args[0];
1423 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001424 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001425 struct wireless_dev *wdev;
1426
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001427 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001428 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1429 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001430 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001431 if (wp_idx < wp_start) {
1432 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001433 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001434 }
Johannes Berg55682962007-09-20 13:09:35 -04001435 if_idx = 0;
1436
Johannes Bergf5ea9122009-08-07 16:17:38 +02001437 mutex_lock(&rdev->devlist_mtx);
1438 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001439 if (if_idx < if_start) {
1440 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001441 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001442 }
Johannes Berg55682962007-09-20 13:09:35 -04001443 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1444 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001445 rdev, wdev->netdev) < 0) {
1446 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001447 goto out;
1448 }
1449 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001450 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001451 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001452
1453 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001454 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001455 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001456 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001457
1458 cb->args[0] = wp_idx;
1459 cb->args[1] = if_idx;
1460
1461 return skb->len;
1462}
1463
1464static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1465{
1466 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001467 struct cfg80211_registered_device *dev = info->user_ptr[0];
1468 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001469
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001470 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001471 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001472 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001473
Johannes Bergd7264052009-04-19 16:23:20 +02001474 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001475 dev, netdev) < 0) {
1476 nlmsg_free(msg);
1477 return -ENOBUFS;
1478 }
Johannes Berg55682962007-09-20 13:09:35 -04001479
Johannes Berg134e6372009-07-10 09:51:34 +00001480 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001481}
1482
Michael Wu66f7ac52008-01-31 19:48:22 +01001483static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1484 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1485 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1486 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1487 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1488 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1489};
1490
1491static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1492{
1493 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1494 int flag;
1495
1496 *mntrflags = 0;
1497
1498 if (!nla)
1499 return -EINVAL;
1500
1501 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1502 nla, mntr_flags_policy))
1503 return -EINVAL;
1504
1505 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1506 if (flags[flag])
1507 *mntrflags |= (1<<flag);
1508
1509 return 0;
1510}
1511
Johannes Berg9bc383d2009-11-19 11:55:19 +01001512static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001513 struct net_device *netdev, u8 use_4addr,
1514 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001515{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001516 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001517 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001518 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001519 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001520 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001521
1522 switch (iftype) {
1523 case NL80211_IFTYPE_AP_VLAN:
1524 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1525 return 0;
1526 break;
1527 case NL80211_IFTYPE_STATION:
1528 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1529 return 0;
1530 break;
1531 default:
1532 break;
1533 }
1534
1535 return -EOPNOTSUPP;
1536}
1537
Johannes Berg55682962007-09-20 13:09:35 -04001538static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1539{
Johannes Berg4c476992010-10-04 21:36:35 +02001540 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001541 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001542 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001543 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001544 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001545 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001546 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001547
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001548 memset(&params, 0, sizeof(params));
1549
Johannes Berg04a773a2009-04-19 21:24:32 +02001550 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001551
Johannes Berg723b0382008-09-16 20:22:09 +02001552 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001553 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001554 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001555 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001556 if (ntype > NL80211_IFTYPE_MAX)
1557 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001558 }
1559
Johannes Berg92ffe052008-09-16 20:39:36 +02001560 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001561 struct wireless_dev *wdev = dev->ieee80211_ptr;
1562
Johannes Berg4c476992010-10-04 21:36:35 +02001563 if (ntype != NL80211_IFTYPE_MESH_POINT)
1564 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001565 if (netif_running(dev))
1566 return -EBUSY;
1567
1568 wdev_lock(wdev);
1569 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1570 IEEE80211_MAX_MESH_ID_LEN);
1571 wdev->mesh_id_up_len =
1572 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1573 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1574 wdev->mesh_id_up_len);
1575 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001576 }
1577
Felix Fietkau8b787642009-11-10 18:53:10 +01001578 if (info->attrs[NL80211_ATTR_4ADDR]) {
1579 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1580 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001581 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001582 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001583 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001584 } else {
1585 params.use_4addr = -1;
1586 }
1587
Johannes Berg92ffe052008-09-16 20:39:36 +02001588 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001589 if (ntype != NL80211_IFTYPE_MONITOR)
1590 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001591 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1592 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001593 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001594 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001595
1596 flags = &_flags;
1597 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001598 }
Johannes Berg3b858752009-03-12 09:55:09 +01001599
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001600 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001601 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001602 else
1603 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001604
Johannes Berg9bc383d2009-11-19 11:55:19 +01001605 if (!err && params.use_4addr != -1)
1606 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1607
Johannes Berg55682962007-09-20 13:09:35 -04001608 return err;
1609}
1610
1611static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1612{
Johannes Berg4c476992010-10-04 21:36:35 +02001613 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001614 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001615 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001616 int err;
1617 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001618 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001619
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001620 memset(&params, 0, sizeof(params));
1621
Johannes Berg55682962007-09-20 13:09:35 -04001622 if (!info->attrs[NL80211_ATTR_IFNAME])
1623 return -EINVAL;
1624
1625 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1626 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1627 if (type > NL80211_IFTYPE_MAX)
1628 return -EINVAL;
1629 }
1630
Johannes Berg79c97e92009-07-07 03:56:12 +02001631 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001632 !(rdev->wiphy.interface_modes & (1 << type)))
1633 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001634
Johannes Berg9bc383d2009-11-19 11:55:19 +01001635 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001636 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001637 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001638 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001639 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001640 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001641
Michael Wu66f7ac52008-01-31 19:48:22 +01001642 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1643 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1644 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001645 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001646 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001647 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001648 if (IS_ERR(dev))
1649 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001650
Johannes Berg29cbe682010-12-03 09:20:44 +01001651 if (type == NL80211_IFTYPE_MESH_POINT &&
1652 info->attrs[NL80211_ATTR_MESH_ID]) {
1653 struct wireless_dev *wdev = dev->ieee80211_ptr;
1654
1655 wdev_lock(wdev);
1656 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1657 IEEE80211_MAX_MESH_ID_LEN);
1658 wdev->mesh_id_up_len =
1659 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1660 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1661 wdev->mesh_id_up_len);
1662 wdev_unlock(wdev);
1663 }
1664
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001665 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001666}
1667
1668static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1669{
Johannes Berg4c476992010-10-04 21:36:35 +02001670 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1671 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001672
Johannes Berg4c476992010-10-04 21:36:35 +02001673 if (!rdev->ops->del_virtual_intf)
1674 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001675
Johannes Berg4c476992010-10-04 21:36:35 +02001676 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001677}
1678
Johannes Berg41ade002007-12-19 02:03:29 +01001679struct get_key_cookie {
1680 struct sk_buff *msg;
1681 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001682 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001683};
1684
1685static void get_key_callback(void *c, struct key_params *params)
1686{
Johannes Bergb9454e82009-07-08 13:29:08 +02001687 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001688 struct get_key_cookie *cookie = c;
1689
1690 if (params->key)
1691 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1692 params->key_len, params->key);
1693
1694 if (params->seq)
1695 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1696 params->seq_len, params->seq);
1697
1698 if (params->cipher)
1699 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1700 params->cipher);
1701
Johannes Bergb9454e82009-07-08 13:29:08 +02001702 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1703 if (!key)
1704 goto nla_put_failure;
1705
1706 if (params->key)
1707 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1708 params->key_len, params->key);
1709
1710 if (params->seq)
1711 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1712 params->seq_len, params->seq);
1713
1714 if (params->cipher)
1715 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1716 params->cipher);
1717
1718 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1719
1720 nla_nest_end(cookie->msg, key);
1721
Johannes Berg41ade002007-12-19 02:03:29 +01001722 return;
1723 nla_put_failure:
1724 cookie->error = 1;
1725}
1726
1727static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1728{
Johannes Berg4c476992010-10-04 21:36:35 +02001729 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001730 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001731 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001732 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001733 const u8 *mac_addr = NULL;
1734 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001735 struct get_key_cookie cookie = {
1736 .error = 0,
1737 };
1738 void *hdr;
1739 struct sk_buff *msg;
1740
1741 if (info->attrs[NL80211_ATTR_KEY_IDX])
1742 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1743
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001744 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001745 return -EINVAL;
1746
1747 if (info->attrs[NL80211_ATTR_MAC])
1748 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1749
Johannes Berge31b8212010-10-05 19:39:30 +02001750 pairwise = !!mac_addr;
1751 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1752 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1753 if (kt >= NUM_NL80211_KEYTYPES)
1754 return -EINVAL;
1755 if (kt != NL80211_KEYTYPE_GROUP &&
1756 kt != NL80211_KEYTYPE_PAIRWISE)
1757 return -EINVAL;
1758 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1759 }
1760
Johannes Berg4c476992010-10-04 21:36:35 +02001761 if (!rdev->ops->get_key)
1762 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001763
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001764 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001765 if (!msg)
1766 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001767
1768 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1769 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001770 if (IS_ERR(hdr))
1771 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001772
1773 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001774 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001775
1776 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1777 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1778 if (mac_addr)
1779 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1780
Johannes Berge31b8212010-10-05 19:39:30 +02001781 if (pairwise && mac_addr &&
1782 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1783 return -ENOENT;
1784
1785 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1786 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001787
1788 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001789 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001790
1791 if (cookie.error)
1792 goto nla_put_failure;
1793
1794 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001795 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001796
1797 nla_put_failure:
1798 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001799 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001800 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001801 return err;
1802}
1803
1804static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1805{
Johannes Berg4c476992010-10-04 21:36:35 +02001806 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001807 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001808 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001809 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001810
Johannes Bergb9454e82009-07-08 13:29:08 +02001811 err = nl80211_parse_key(info, &key);
1812 if (err)
1813 return err;
1814
1815 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001816 return -EINVAL;
1817
Johannes Bergb9454e82009-07-08 13:29:08 +02001818 /* only support setting default key */
1819 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001820 return -EINVAL;
1821
Johannes Bergfffd0932009-07-08 14:22:54 +02001822 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001823
1824 if (key.def) {
1825 if (!rdev->ops->set_default_key) {
1826 err = -EOPNOTSUPP;
1827 goto out;
1828 }
1829
1830 err = nl80211_key_allowed(dev->ieee80211_ptr);
1831 if (err)
1832 goto out;
1833
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001834 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
1835 key.def_uni, key.def_multi);
1836
1837 if (err)
1838 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02001839
Johannes Berg3d23e342009-09-29 23:27:28 +02001840#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001841 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001842#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001843 } else {
1844 if (key.def_uni || !key.def_multi) {
1845 err = -EINVAL;
1846 goto out;
1847 }
1848
1849 if (!rdev->ops->set_default_mgmt_key) {
1850 err = -EOPNOTSUPP;
1851 goto out;
1852 }
1853
1854 err = nl80211_key_allowed(dev->ieee80211_ptr);
1855 if (err)
1856 goto out;
1857
1858 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
1859 dev, key.idx);
1860 if (err)
1861 goto out;
1862
1863#ifdef CONFIG_CFG80211_WEXT
1864 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
1865#endif
1866 }
1867
1868 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02001869 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001870
Johannes Berg41ade002007-12-19 02:03:29 +01001871 return err;
1872}
1873
1874static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1875{
Johannes Berg4c476992010-10-04 21:36:35 +02001876 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02001877 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001878 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02001879 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02001880 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01001881
Johannes Bergb9454e82009-07-08 13:29:08 +02001882 err = nl80211_parse_key(info, &key);
1883 if (err)
1884 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001885
Johannes Bergb9454e82009-07-08 13:29:08 +02001886 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001887 return -EINVAL;
1888
Johannes Berg41ade002007-12-19 02:03:29 +01001889 if (info->attrs[NL80211_ATTR_MAC])
1890 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1891
Johannes Berge31b8212010-10-05 19:39:30 +02001892 if (key.type == -1) {
1893 if (mac_addr)
1894 key.type = NL80211_KEYTYPE_PAIRWISE;
1895 else
1896 key.type = NL80211_KEYTYPE_GROUP;
1897 }
1898
1899 /* for now */
1900 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
1901 key.type != NL80211_KEYTYPE_GROUP)
1902 return -EINVAL;
1903
Johannes Berg4c476992010-10-04 21:36:35 +02001904 if (!rdev->ops->add_key)
1905 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001906
Johannes Berge31b8212010-10-05 19:39:30 +02001907 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
1908 key.type == NL80211_KEYTYPE_PAIRWISE,
1909 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02001910 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02001911
1912 wdev_lock(dev->ieee80211_ptr);
1913 err = nl80211_key_allowed(dev->ieee80211_ptr);
1914 if (!err)
1915 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02001916 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02001917 mac_addr, &key.p);
1918 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001919
Johannes Berg41ade002007-12-19 02:03:29 +01001920 return err;
1921}
1922
1923static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1924{
Johannes Berg4c476992010-10-04 21:36:35 +02001925 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001926 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001927 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001928 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001929 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001930
Johannes Bergb9454e82009-07-08 13:29:08 +02001931 err = nl80211_parse_key(info, &key);
1932 if (err)
1933 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001934
1935 if (info->attrs[NL80211_ATTR_MAC])
1936 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1937
Johannes Berge31b8212010-10-05 19:39:30 +02001938 if (key.type == -1) {
1939 if (mac_addr)
1940 key.type = NL80211_KEYTYPE_PAIRWISE;
1941 else
1942 key.type = NL80211_KEYTYPE_GROUP;
1943 }
1944
1945 /* for now */
1946 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
1947 key.type != NL80211_KEYTYPE_GROUP)
1948 return -EINVAL;
1949
Johannes Berg4c476992010-10-04 21:36:35 +02001950 if (!rdev->ops->del_key)
1951 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001952
Johannes Bergfffd0932009-07-08 14:22:54 +02001953 wdev_lock(dev->ieee80211_ptr);
1954 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02001955
1956 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
1957 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1958 err = -ENOENT;
1959
Johannes Bergfffd0932009-07-08 14:22:54 +02001960 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02001961 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
1962 key.type == NL80211_KEYTYPE_PAIRWISE,
1963 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01001964
Johannes Berg3d23e342009-09-29 23:27:28 +02001965#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02001966 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02001967 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02001968 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02001969 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02001970 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
1971 }
1972#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02001973 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02001974
Johannes Berg41ade002007-12-19 02:03:29 +01001975 return err;
1976}
1977
Johannes Berged1b6cc2007-12-19 02:03:32 +01001978static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
1979{
1980 int (*call)(struct wiphy *wiphy, struct net_device *dev,
1981 struct beacon_parameters *info);
Johannes Berg4c476992010-10-04 21:36:35 +02001982 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1983 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02001984 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001985 struct beacon_parameters params;
Johannes Berg56d18932011-05-09 18:41:15 +02001986 int haveinfo = 0, err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01001987
Johannes Bergf4a11bb2009-03-27 12:40:28 +01001988 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]))
1989 return -EINVAL;
1990
Johannes Berg074ac8d2010-09-16 14:58:22 +02001991 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02001992 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
1993 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02001994
Johannes Berg56d18932011-05-09 18:41:15 +02001995 memset(&params, 0, sizeof(params));
1996
Johannes Berged1b6cc2007-12-19 02:03:32 +01001997 switch (info->genlhdr->cmd) {
1998 case NL80211_CMD_NEW_BEACON:
1999 /* these are required for NEW_BEACON */
2000 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2001 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
Johannes Berg4c476992010-10-04 21:36:35 +02002002 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2003 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002004
Johannes Berg56d18932011-05-09 18:41:15 +02002005 params.interval =
2006 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2007 params.dtim_period =
2008 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2009
2010 err = cfg80211_validate_beacon_int(rdev, params.interval);
2011 if (err)
2012 return err;
2013
Jouni Malinen32e9de82011-08-10 23:53:31 +03002014 /*
2015 * In theory, some of these attributes could be required for
2016 * NEW_BEACON, but since they were not used when the command was
2017 * originally added, keep them optional for old user space
2018 * programs to work with drivers that do not need the additional
2019 * information.
2020 */
2021 if (info->attrs[NL80211_ATTR_SSID]) {
2022 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2023 params.ssid_len =
2024 nla_len(info->attrs[NL80211_ATTR_SSID]);
2025 if (params.ssid_len == 0 ||
2026 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2027 return -EINVAL;
2028 }
2029
2030 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2031 params.hidden_ssid = nla_get_u32(
2032 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2033 if (params.hidden_ssid !=
2034 NL80211_HIDDEN_SSID_NOT_IN_USE &&
2035 params.hidden_ssid !=
2036 NL80211_HIDDEN_SSID_ZERO_LEN &&
2037 params.hidden_ssid !=
2038 NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2039 return -EINVAL;
2040 }
2041
Johannes Berg79c97e92009-07-07 03:56:12 +02002042 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002043 break;
2044 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02002045 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002046 break;
2047 default:
2048 WARN_ON(1);
Johannes Berg4c476992010-10-04 21:36:35 +02002049 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002050 }
2051
Johannes Berg4c476992010-10-04 21:36:35 +02002052 if (!call)
2053 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002054
Johannes Berged1b6cc2007-12-19 02:03:32 +01002055 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
2056 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2057 params.head_len =
2058 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2059 haveinfo = 1;
2060 }
2061
2062 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
2063 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2064 params.tail_len =
2065 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2066 haveinfo = 1;
2067 }
2068
Johannes Berg4c476992010-10-04 21:36:35 +02002069 if (!haveinfo)
2070 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002071
Johannes Berg56d18932011-05-09 18:41:15 +02002072 err = call(&rdev->wiphy, dev, &params);
2073 if (!err && params.interval)
2074 wdev->beacon_interval = params.interval;
2075 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002076}
2077
2078static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
2079{
Johannes Berg4c476992010-10-04 21:36:35 +02002080 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2081 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002082 struct wireless_dev *wdev = dev->ieee80211_ptr;
2083 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002084
Johannes Berg4c476992010-10-04 21:36:35 +02002085 if (!rdev->ops->del_beacon)
2086 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002087
Johannes Berg074ac8d2010-09-16 14:58:22 +02002088 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002089 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2090 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002091
Johannes Berg56d18932011-05-09 18:41:15 +02002092 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
2093 if (!err)
2094 wdev->beacon_interval = 0;
2095 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002096}
2097
Johannes Berg5727ef12007-12-19 02:03:34 +01002098static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2099 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2100 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2101 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002102 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002103 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002104};
2105
Johannes Bergeccb8e82009-05-11 21:57:56 +03002106static int parse_station_flags(struct genl_info *info,
2107 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002108{
2109 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002110 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002111 int flag;
2112
Johannes Bergeccb8e82009-05-11 21:57:56 +03002113 /*
2114 * Try parsing the new attribute first so userspace
2115 * can specify both for older kernels.
2116 */
2117 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2118 if (nla) {
2119 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002120
Johannes Bergeccb8e82009-05-11 21:57:56 +03002121 sta_flags = nla_data(nla);
2122 params->sta_flags_mask = sta_flags->mask;
2123 params->sta_flags_set = sta_flags->set;
2124 if ((params->sta_flags_mask |
2125 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2126 return -EINVAL;
2127 return 0;
2128 }
2129
2130 /* if present, parse the old attribute */
2131
2132 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002133 if (!nla)
2134 return 0;
2135
2136 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2137 nla, sta_flags_policy))
2138 return -EINVAL;
2139
Johannes Bergeccb8e82009-05-11 21:57:56 +03002140 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
2141 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002142
2143 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
2144 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03002145 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002146
2147 return 0;
2148}
2149
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002150static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2151 int attr)
2152{
2153 struct nlattr *rate;
2154 u16 bitrate;
2155
2156 rate = nla_nest_start(msg, attr);
2157 if (!rate)
2158 goto nla_put_failure;
2159
2160 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2161 bitrate = cfg80211_calculate_bitrate(info);
2162 if (bitrate > 0)
2163 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
2164
2165 if (info->flags & RATE_INFO_FLAGS_MCS)
2166 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs);
2167 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
2168 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
2169 if (info->flags & RATE_INFO_FLAGS_SHORT_GI)
2170 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
2171
2172 nla_nest_end(msg, rate);
2173 return true;
2174
2175nla_put_failure:
2176 return false;
2177}
2178
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002179static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
2180 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002181 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002182{
2183 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002184 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002185
2186 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2187 if (!hdr)
2188 return -1;
2189
2190 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2191 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
2192
Johannes Bergf5ea9122009-08-07 16:17:38 +02002193 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
2194
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002195 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2196 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002197 goto nla_put_failure;
Mohammed Shafi Shajakhanebe27c92011-04-08 21:24:24 +05302198 if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
2199 NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2200 sinfo->connected_time);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002201 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
2202 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2203 sinfo->inactive_time);
2204 if (sinfo->filled & STATION_INFO_RX_BYTES)
2205 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
2206 sinfo->rx_bytes);
2207 if (sinfo->filled & STATION_INFO_TX_BYTES)
2208 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
2209 sinfo->tx_bytes);
2210 if (sinfo->filled & STATION_INFO_LLID)
2211 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
2212 sinfo->llid);
2213 if (sinfo->filled & STATION_INFO_PLID)
2214 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
2215 sinfo->plid);
2216 if (sinfo->filled & STATION_INFO_PLINK_STATE)
2217 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
2218 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002219 if (sinfo->filled & STATION_INFO_SIGNAL)
2220 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
2221 sinfo->signal);
Bruno Randolf541a45a2010-12-02 19:12:43 +09002222 if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
2223 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2224 sinfo->signal_avg);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002225 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002226 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2227 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002228 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002229 }
2230 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2231 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2232 NL80211_STA_INFO_RX_BITRATE))
2233 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002234 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02002235 if (sinfo->filled & STATION_INFO_RX_PACKETS)
2236 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
2237 sinfo->rx_packets);
2238 if (sinfo->filled & STATION_INFO_TX_PACKETS)
2239 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
2240 sinfo->tx_packets);
Bruno Randolfb206b4e2010-10-06 18:34:12 +09002241 if (sinfo->filled & STATION_INFO_TX_RETRIES)
2242 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES,
2243 sinfo->tx_retries);
2244 if (sinfo->filled & STATION_INFO_TX_FAILED)
2245 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
2246 sinfo->tx_failed);
Paul Stewartf4263c92011-03-31 09:25:41 -07002247 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2248 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2249 if (!bss_param)
2250 goto nla_put_failure;
2251
2252 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
2253 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
2254 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
2255 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
2256 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
2257 NLA_PUT_FLAG(msg,
2258 NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
2259 NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2260 sinfo->bss_param.dtim_period);
2261 NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2262 sinfo->bss_param.beacon_interval);
2263
2264 nla_nest_end(msg, bss_param);
2265 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002266 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002267
Felix Fietkau040bdf72011-08-10 19:00:33 -06002268 if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002269 NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2270 sinfo->assoc_req_ies);
2271
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002272 return genlmsg_end(msg, hdr);
2273
2274 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002275 genlmsg_cancel(msg, hdr);
2276 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002277}
2278
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002279static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002280 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002281{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002282 struct station_info sinfo;
2283 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002284 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002285 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002286 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002287 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002288
Johannes Berg67748892010-10-04 21:14:06 +02002289 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2290 if (err)
2291 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002292
2293 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002294 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002295 goto out_err;
2296 }
2297
Johannes Bergbba95fe2008-07-29 13:22:51 +02002298 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002299 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002300 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2301 mac_addr, &sinfo);
2302 if (err == -ENOENT)
2303 break;
2304 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002305 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002306
2307 if (nl80211_send_station(skb,
2308 NETLINK_CB(cb->skb).pid,
2309 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2310 netdev, mac_addr,
2311 &sinfo) < 0)
2312 goto out;
2313
2314 sta_idx++;
2315 }
2316
2317
2318 out:
2319 cb->args[1] = sta_idx;
2320 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002321 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002322 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002323
2324 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002325}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002326
Johannes Berg5727ef12007-12-19 02:03:34 +01002327static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2328{
Johannes Berg4c476992010-10-04 21:36:35 +02002329 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2330 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002331 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002332 struct sk_buff *msg;
2333 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002334 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002335
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002336 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002337
2338 if (!info->attrs[NL80211_ATTR_MAC])
2339 return -EINVAL;
2340
2341 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2342
Johannes Berg4c476992010-10-04 21:36:35 +02002343 if (!rdev->ops->get_station)
2344 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002345
Johannes Berg79c97e92009-07-07 03:56:12 +02002346 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002347 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002348 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002349
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002350 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002351 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002352 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002353
2354 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002355 dev, mac_addr, &sinfo) < 0) {
2356 nlmsg_free(msg);
2357 return -ENOBUFS;
2358 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002359
Johannes Berg4c476992010-10-04 21:36:35 +02002360 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002361}
2362
2363/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002364 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002365 */
Johannes Berg463d0182009-07-14 00:33:35 +02002366static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01002367 struct cfg80211_registered_device *rdev,
2368 struct net_device **vlan)
2369{
Johannes Berg463d0182009-07-14 00:33:35 +02002370 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01002371 *vlan = NULL;
2372
2373 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02002374 *vlan = dev_get_by_index(genl_info_net(info),
2375 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01002376 if (!*vlan)
2377 return -ENODEV;
2378 if (!(*vlan)->ieee80211_ptr)
2379 return -EINVAL;
2380 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
2381 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002382 if (!netif_running(*vlan))
2383 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01002384 }
2385 return 0;
2386}
2387
2388static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2389{
Johannes Berg4c476992010-10-04 21:36:35 +02002390 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002391 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002392 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002393 struct station_parameters params;
2394 u8 *mac_addr = NULL;
2395
2396 memset(&params, 0, sizeof(params));
2397
2398 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002399 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002400
2401 if (info->attrs[NL80211_ATTR_STA_AID])
2402 return -EINVAL;
2403
2404 if (!info->attrs[NL80211_ATTR_MAC])
2405 return -EINVAL;
2406
2407 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2408
2409 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2410 params.supported_rates =
2411 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2412 params.supported_rates_len =
2413 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2414 }
2415
2416 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2417 params.listen_interval =
2418 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2419
Jouni Malinen36aedc92008-08-25 11:58:58 +03002420 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2421 params.ht_capa =
2422 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2423
Johannes Bergeccb8e82009-05-11 21:57:56 +03002424 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002425 return -EINVAL;
2426
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002427 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2428 params.plink_action =
2429 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2430
Javier Cardona9c3990a2011-05-03 16:57:11 -07002431 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2432 params.plink_state =
2433 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2434
Johannes Berg463d0182009-07-14 00:33:35 +02002435 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002436 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02002437 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002438
2439 /* validate settings */
2440 err = 0;
2441
2442 switch (dev->ieee80211_ptr->iftype) {
2443 case NL80211_IFTYPE_AP:
2444 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002445 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002446 /* disallow mesh-specific things */
2447 if (params.plink_action)
2448 err = -EINVAL;
2449 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002450 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002451 case NL80211_IFTYPE_STATION:
2452 /* disallow everything but AUTHORIZED flag */
2453 if (params.plink_action)
2454 err = -EINVAL;
2455 if (params.vlan)
2456 err = -EINVAL;
2457 if (params.supported_rates)
2458 err = -EINVAL;
2459 if (params.ht_capa)
2460 err = -EINVAL;
2461 if (params.listen_interval >= 0)
2462 err = -EINVAL;
2463 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2464 err = -EINVAL;
2465 break;
2466 case NL80211_IFTYPE_MESH_POINT:
2467 /* disallow things mesh doesn't support */
2468 if (params.vlan)
2469 err = -EINVAL;
2470 if (params.ht_capa)
2471 err = -EINVAL;
2472 if (params.listen_interval >= 0)
2473 err = -EINVAL;
Javier Cardonab39c48f2011-04-07 15:08:30 -07002474 if (params.sta_flags_mask &
2475 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002476 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002477 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Berga97f4422009-06-18 17:23:43 +02002478 err = -EINVAL;
2479 break;
2480 default:
2481 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02002482 }
2483
Johannes Berg5727ef12007-12-19 02:03:34 +01002484 if (err)
2485 goto out;
2486
Johannes Berg79c97e92009-07-07 03:56:12 +02002487 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002488 err = -EOPNOTSUPP;
2489 goto out;
2490 }
2491
Johannes Berg79c97e92009-07-07 03:56:12 +02002492 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002493
2494 out:
2495 if (params.vlan)
2496 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002497
Johannes Berg5727ef12007-12-19 02:03:34 +01002498 return err;
2499}
2500
2501static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2502{
Johannes Berg4c476992010-10-04 21:36:35 +02002503 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002504 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002505 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002506 struct station_parameters params;
2507 u8 *mac_addr = NULL;
2508
2509 memset(&params, 0, sizeof(params));
2510
2511 if (!info->attrs[NL80211_ATTR_MAC])
2512 return -EINVAL;
2513
Johannes Berg5727ef12007-12-19 02:03:34 +01002514 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2515 return -EINVAL;
2516
2517 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2518 return -EINVAL;
2519
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002520 if (!info->attrs[NL80211_ATTR_STA_AID])
2521 return -EINVAL;
2522
Johannes Berg5727ef12007-12-19 02:03:34 +01002523 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2524 params.supported_rates =
2525 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2526 params.supported_rates_len =
2527 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2528 params.listen_interval =
2529 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002530
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002531 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2532 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2533 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002534
Jouni Malinen36aedc92008-08-25 11:58:58 +03002535 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2536 params.ht_capa =
2537 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002538
Javier Cardona96b78df2011-04-07 15:08:33 -07002539 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2540 params.plink_action =
2541 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2542
Johannes Bergeccb8e82009-05-11 21:57:56 +03002543 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002544 return -EINVAL;
2545
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002546 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002547 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardona96b78df2011-04-07 15:08:33 -07002548 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002549 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2550 return -EINVAL;
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002551
Johannes Berg463d0182009-07-14 00:33:35 +02002552 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002553 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002554 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002555
2556 /* validate settings */
2557 err = 0;
2558
Johannes Berg79c97e92009-07-07 03:56:12 +02002559 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002560 err = -EOPNOTSUPP;
2561 goto out;
2562 }
2563
Johannes Berg79c97e92009-07-07 03:56:12 +02002564 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002565
2566 out:
2567 if (params.vlan)
2568 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01002569 return err;
2570}
2571
2572static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2573{
Johannes Berg4c476992010-10-04 21:36:35 +02002574 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2575 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002576 u8 *mac_addr = NULL;
2577
2578 if (info->attrs[NL80211_ATTR_MAC])
2579 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2580
Johannes Berge80cf852009-05-11 14:43:13 +02002581 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002582 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002583 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002584 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2585 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002586
Johannes Berg4c476992010-10-04 21:36:35 +02002587 if (!rdev->ops->del_station)
2588 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01002589
Johannes Berg4c476992010-10-04 21:36:35 +02002590 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002591}
2592
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002593static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2594 int flags, struct net_device *dev,
2595 u8 *dst, u8 *next_hop,
2596 struct mpath_info *pinfo)
2597{
2598 void *hdr;
2599 struct nlattr *pinfoattr;
2600
2601 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2602 if (!hdr)
2603 return -1;
2604
2605 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2606 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2607 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2608
Johannes Bergf5ea9122009-08-07 16:17:38 +02002609 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2610
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002611 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2612 if (!pinfoattr)
2613 goto nla_put_failure;
2614 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2615 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2616 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002617 if (pinfo->filled & MPATH_INFO_SN)
2618 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2619 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002620 if (pinfo->filled & MPATH_INFO_METRIC)
2621 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2622 pinfo->metric);
2623 if (pinfo->filled & MPATH_INFO_EXPTIME)
2624 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2625 pinfo->exptime);
2626 if (pinfo->filled & MPATH_INFO_FLAGS)
2627 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2628 pinfo->flags);
2629 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2630 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2631 pinfo->discovery_timeout);
2632 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2633 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2634 pinfo->discovery_retries);
2635
2636 nla_nest_end(msg, pinfoattr);
2637
2638 return genlmsg_end(msg, hdr);
2639
2640 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002641 genlmsg_cancel(msg, hdr);
2642 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002643}
2644
2645static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002646 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002647{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002648 struct mpath_info pinfo;
2649 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002650 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002651 u8 dst[ETH_ALEN];
2652 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002653 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002654 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002655
Johannes Berg67748892010-10-04 21:14:06 +02002656 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2657 if (err)
2658 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002659
2660 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002661 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002662 goto out_err;
2663 }
2664
Jouni Malineneec60b02009-03-20 21:21:19 +02002665 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2666 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002667 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002668 }
2669
Johannes Bergbba95fe2008-07-29 13:22:51 +02002670 while (1) {
2671 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2672 dst, next_hop, &pinfo);
2673 if (err == -ENOENT)
2674 break;
2675 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002676 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002677
2678 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2679 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2680 netdev, dst, next_hop,
2681 &pinfo) < 0)
2682 goto out;
2683
2684 path_idx++;
2685 }
2686
2687
2688 out:
2689 cb->args[1] = path_idx;
2690 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002691 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002692 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002693 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002694}
2695
2696static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2697{
Johannes Berg4c476992010-10-04 21:36:35 +02002698 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002699 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002700 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002701 struct mpath_info pinfo;
2702 struct sk_buff *msg;
2703 u8 *dst = NULL;
2704 u8 next_hop[ETH_ALEN];
2705
2706 memset(&pinfo, 0, sizeof(pinfo));
2707
2708 if (!info->attrs[NL80211_ATTR_MAC])
2709 return -EINVAL;
2710
2711 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2712
Johannes Berg4c476992010-10-04 21:36:35 +02002713 if (!rdev->ops->get_mpath)
2714 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002715
Johannes Berg4c476992010-10-04 21:36:35 +02002716 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2717 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002718
Johannes Berg79c97e92009-07-07 03:56:12 +02002719 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002720 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002721 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002722
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002723 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002724 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002725 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002726
2727 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002728 dev, dst, next_hop, &pinfo) < 0) {
2729 nlmsg_free(msg);
2730 return -ENOBUFS;
2731 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002732
Johannes Berg4c476992010-10-04 21:36:35 +02002733 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002734}
2735
2736static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2737{
Johannes Berg4c476992010-10-04 21:36:35 +02002738 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2739 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002740 u8 *dst = NULL;
2741 u8 *next_hop = NULL;
2742
2743 if (!info->attrs[NL80211_ATTR_MAC])
2744 return -EINVAL;
2745
2746 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2747 return -EINVAL;
2748
2749 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2750 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2751
Johannes Berg4c476992010-10-04 21:36:35 +02002752 if (!rdev->ops->change_mpath)
2753 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002754
Johannes Berg4c476992010-10-04 21:36:35 +02002755 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2756 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002757
Johannes Berg4c476992010-10-04 21:36:35 +02002758 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002759}
Johannes Berg4c476992010-10-04 21:36:35 +02002760
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002761static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2762{
Johannes Berg4c476992010-10-04 21:36:35 +02002763 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2764 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002765 u8 *dst = NULL;
2766 u8 *next_hop = NULL;
2767
2768 if (!info->attrs[NL80211_ATTR_MAC])
2769 return -EINVAL;
2770
2771 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2772 return -EINVAL;
2773
2774 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2775 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2776
Johannes Berg4c476992010-10-04 21:36:35 +02002777 if (!rdev->ops->add_mpath)
2778 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002779
Johannes Berg4c476992010-10-04 21:36:35 +02002780 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2781 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002782
Johannes Berg4c476992010-10-04 21:36:35 +02002783 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002784}
2785
2786static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2787{
Johannes Berg4c476992010-10-04 21:36:35 +02002788 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2789 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002790 u8 *dst = NULL;
2791
2792 if (info->attrs[NL80211_ATTR_MAC])
2793 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2794
Johannes Berg4c476992010-10-04 21:36:35 +02002795 if (!rdev->ops->del_mpath)
2796 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002797
Johannes Berg4c476992010-10-04 21:36:35 +02002798 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002799}
2800
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002801static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2802{
Johannes Berg4c476992010-10-04 21:36:35 +02002803 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2804 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002805 struct bss_parameters params;
2806
2807 memset(&params, 0, sizeof(params));
2808 /* default to not changing parameters */
2809 params.use_cts_prot = -1;
2810 params.use_short_preamble = -1;
2811 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002812 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01002813 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002814
2815 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2816 params.use_cts_prot =
2817 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2818 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2819 params.use_short_preamble =
2820 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2821 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2822 params.use_short_slot_time =
2823 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002824 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2825 params.basic_rates =
2826 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2827 params.basic_rates_len =
2828 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2829 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002830 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
2831 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01002832 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
2833 params.ht_opmode =
2834 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002835
Johannes Berg4c476992010-10-04 21:36:35 +02002836 if (!rdev->ops->change_bss)
2837 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002838
Johannes Berg074ac8d2010-09-16 14:58:22 +02002839 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002840 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2841 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002842
Johannes Berg4c476992010-10-04 21:36:35 +02002843 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002844}
2845
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002846static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002847 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
2848 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
2849 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
2850 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
2851 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
2852 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
2853};
2854
2855static int parse_reg_rule(struct nlattr *tb[],
2856 struct ieee80211_reg_rule *reg_rule)
2857{
2858 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
2859 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
2860
2861 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
2862 return -EINVAL;
2863 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
2864 return -EINVAL;
2865 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
2866 return -EINVAL;
2867 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
2868 return -EINVAL;
2869 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
2870 return -EINVAL;
2871
2872 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
2873
2874 freq_range->start_freq_khz =
2875 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
2876 freq_range->end_freq_khz =
2877 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
2878 freq_range->max_bandwidth_khz =
2879 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
2880
2881 power_rule->max_eirp =
2882 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
2883
2884 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
2885 power_rule->max_antenna_gain =
2886 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
2887
2888 return 0;
2889}
2890
2891static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
2892{
2893 int r;
2894 char *data = NULL;
2895
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002896 /*
2897 * You should only get this when cfg80211 hasn't yet initialized
2898 * completely when built-in to the kernel right between the time
2899 * window between nl80211_init() and regulatory_init(), if that is
2900 * even possible.
2901 */
2902 mutex_lock(&cfg80211_mutex);
2903 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002904 mutex_unlock(&cfg80211_mutex);
2905 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002906 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002907 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05002908
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002909 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
2910 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002911
2912 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
2913
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05002914 r = regulatory_hint_user(data);
2915
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07002916 return r;
2917}
2918
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002919static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01002920 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002921{
Johannes Berg4c476992010-10-04 21:36:35 +02002922 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02002923 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01002924 struct wireless_dev *wdev = dev->ieee80211_ptr;
2925 struct mesh_config cur_params;
2926 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002927 void *hdr;
2928 struct nlattr *pinfoattr;
2929 struct sk_buff *msg;
2930
Johannes Berg29cbe682010-12-03 09:20:44 +01002931 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
2932 return -EOPNOTSUPP;
2933
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002934 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02002935 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02002936
Johannes Berg29cbe682010-12-03 09:20:44 +01002937 wdev_lock(wdev);
2938 /* If not connected, get default parameters */
2939 if (!wdev->mesh_id_len)
2940 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
2941 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002942 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01002943 &cur_params);
2944 wdev_unlock(wdev);
2945
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002946 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002947 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002948
2949 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002950 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002951 if (!msg)
2952 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002953 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002954 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002955 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01002956 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08002957 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002958 if (!pinfoattr)
2959 goto nla_put_failure;
2960 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2961 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
2962 cur_params.dot11MeshRetryTimeout);
2963 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
2964 cur_params.dot11MeshConfirmTimeout);
2965 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
2966 cur_params.dot11MeshHoldingTimeout);
2967 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
2968 cur_params.dot11MeshMaxPeerLinks);
2969 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
2970 cur_params.dot11MeshMaxRetries);
2971 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
2972 cur_params.dot11MeshTTL);
Javier Cardona45904f22010-12-03 09:20:40 +01002973 NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL,
2974 cur_params.element_ttl);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002975 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
2976 cur_params.auto_open_plinks);
2977 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
2978 cur_params.dot11MeshHWMPmaxPREQretries);
2979 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
2980 cur_params.path_refresh_time);
2981 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
2982 cur_params.min_discovery_timeout);
2983 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
2984 cur_params.dot11MeshHWMPactivePathTimeout);
2985 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
2986 cur_params.dot11MeshHWMPpreqMinInterval);
2987 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
2988 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00002989 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
2990 cur_params.dot11MeshHWMPRootMode);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002991 nla_nest_end(msg, pinfoattr);
2992 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02002993 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002994
Johannes Berg3b858752009-03-12 09:55:09 +01002995 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07002996 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01002997 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04002998 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02002999 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003000}
3001
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003002static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003003 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3004 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3005 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3006 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3007 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3008 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003009 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003010 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
3011
3012 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3013 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3014 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3015 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3016 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
3017 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
3018};
3019
Javier Cardonac80d5452010-12-16 17:37:49 -08003020static const struct nla_policy
3021 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
3022 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3023 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003024 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003025 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Javier Cardonac80d5452010-12-16 17:37:49 -08003026 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003027 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003028};
3029
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003030static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003031 struct mesh_config *cfg,
3032 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003033{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003034 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003035 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003036
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003037#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3038do {\
3039 if (table[attr_num]) {\
3040 cfg->param = nla_fn(table[attr_num]); \
3041 mask |= (1 << (attr_num - 1)); \
3042 } \
3043} while (0);\
3044
3045
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003046 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003047 return -EINVAL;
3048 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003049 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003050 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003051 return -EINVAL;
3052
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003053 /* This makes sure that there aren't more than 32 mesh config
3054 * parameters (otherwise our bitfield scheme would not work.) */
3055 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3056
3057 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003058 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
3059 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
3060 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
3061 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
3062 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
3063 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
3064 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
3065 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
3066 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
3067 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
3068 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
3069 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003070 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
3071 mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003072 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
3073 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
3074 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
3075 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3076 nla_get_u8);
3077 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
3078 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
3079 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
3080 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3081 nla_get_u16);
3082 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
3083 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3084 nla_get_u32);
3085 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
3086 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3087 nla_get_u16);
3088 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3089 dot11MeshHWMPnetDiameterTraversalTime,
3090 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3091 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003092 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3093 dot11MeshHWMPRootMode, mask,
3094 NL80211_MESHCONF_HWMP_ROOTMODE,
3095 nla_get_u8);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003096 if (mask_out)
3097 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003098
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003099 return 0;
3100
3101#undef FILL_IN_MESH_PARAM_IF_SET
3102}
3103
Javier Cardonac80d5452010-12-16 17:37:49 -08003104static int nl80211_parse_mesh_setup(struct genl_info *info,
3105 struct mesh_setup *setup)
3106{
3107 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3108
3109 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3110 return -EINVAL;
3111 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3112 info->attrs[NL80211_ATTR_MESH_SETUP],
3113 nl80211_mesh_setup_params_policy))
3114 return -EINVAL;
3115
3116 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3117 setup->path_sel_proto =
3118 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3119 IEEE80211_PATH_PROTOCOL_VENDOR :
3120 IEEE80211_PATH_PROTOCOL_HWMP;
3121
3122 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3123 setup->path_metric =
3124 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3125 IEEE80211_PATH_METRIC_VENDOR :
3126 IEEE80211_PATH_METRIC_AIRTIME;
3127
Javier Cardona581a8b02011-04-07 15:08:27 -07003128
3129 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003130 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003131 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003132 if (!is_valid_ie_attr(ieattr))
3133 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003134 setup->ie = nla_data(ieattr);
3135 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003136 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003137 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3138 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003139
3140 return 0;
3141}
3142
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003143static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003144 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003145{
3146 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3147 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003148 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003149 struct mesh_config cfg;
3150 u32 mask;
3151 int err;
3152
Johannes Berg29cbe682010-12-03 09:20:44 +01003153 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3154 return -EOPNOTSUPP;
3155
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003156 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003157 return -EOPNOTSUPP;
3158
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003159 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003160 if (err)
3161 return err;
3162
Johannes Berg29cbe682010-12-03 09:20:44 +01003163 wdev_lock(wdev);
3164 if (!wdev->mesh_id_len)
3165 err = -ENOLINK;
3166
3167 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003168 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003169 mask, &cfg);
3170
3171 wdev_unlock(wdev);
3172
3173 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003174}
3175
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003176static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3177{
3178 struct sk_buff *msg;
3179 void *hdr = NULL;
3180 struct nlattr *nl_reg_rules;
3181 unsigned int i;
3182 int err = -EINVAL;
3183
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003184 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003185
3186 if (!cfg80211_regdomain)
3187 goto out;
3188
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003189 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003190 if (!msg) {
3191 err = -ENOBUFS;
3192 goto out;
3193 }
3194
3195 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3196 NL80211_CMD_GET_REG);
3197 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003198 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003199
3200 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3201 cfg80211_regdomain->alpha2);
3202
3203 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3204 if (!nl_reg_rules)
3205 goto nla_put_failure;
3206
3207 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3208 struct nlattr *nl_reg_rule;
3209 const struct ieee80211_reg_rule *reg_rule;
3210 const struct ieee80211_freq_range *freq_range;
3211 const struct ieee80211_power_rule *power_rule;
3212
3213 reg_rule = &cfg80211_regdomain->reg_rules[i];
3214 freq_range = &reg_rule->freq_range;
3215 power_rule = &reg_rule->power_rule;
3216
3217 nl_reg_rule = nla_nest_start(msg, i);
3218 if (!nl_reg_rule)
3219 goto nla_put_failure;
3220
3221 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3222 reg_rule->flags);
3223 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3224 freq_range->start_freq_khz);
3225 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3226 freq_range->end_freq_khz);
3227 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3228 freq_range->max_bandwidth_khz);
3229 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3230 power_rule->max_antenna_gain);
3231 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3232 power_rule->max_eirp);
3233
3234 nla_nest_end(msg, nl_reg_rule);
3235 }
3236
3237 nla_nest_end(msg, nl_reg_rules);
3238
3239 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003240 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003241 goto out;
3242
3243nla_put_failure:
3244 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003245put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003246 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003247 err = -EMSGSIZE;
3248out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003249 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003250 return err;
3251}
3252
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003253static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3254{
3255 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3256 struct nlattr *nl_reg_rule;
3257 char *alpha2 = NULL;
3258 int rem_reg_rules = 0, r = 0;
3259 u32 num_rules = 0, rule_idx = 0, size_of_regd;
3260 struct ieee80211_regdomain *rd = NULL;
3261
3262 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3263 return -EINVAL;
3264
3265 if (!info->attrs[NL80211_ATTR_REG_RULES])
3266 return -EINVAL;
3267
3268 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3269
3270 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3271 rem_reg_rules) {
3272 num_rules++;
3273 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003274 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003275 }
3276
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003277 mutex_lock(&cfg80211_mutex);
3278
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003279 if (!reg_is_valid_request(alpha2)) {
3280 r = -EINVAL;
3281 goto bad_reg;
3282 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003283
3284 size_of_regd = sizeof(struct ieee80211_regdomain) +
3285 (num_rules * sizeof(struct ieee80211_reg_rule));
3286
3287 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003288 if (!rd) {
3289 r = -ENOMEM;
3290 goto bad_reg;
3291 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003292
3293 rd->n_reg_rules = num_rules;
3294 rd->alpha2[0] = alpha2[0];
3295 rd->alpha2[1] = alpha2[1];
3296
3297 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3298 rem_reg_rules) {
3299 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3300 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3301 reg_rule_policy);
3302 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3303 if (r)
3304 goto bad_reg;
3305
3306 rule_idx++;
3307
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003308 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3309 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003310 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003311 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003312 }
3313
3314 BUG_ON(rule_idx != num_rules);
3315
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003316 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003317
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003318 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003319
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003320 return r;
3321
Johannes Bergd2372b32008-10-24 20:32:20 +02003322 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003323 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003324 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003325 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003326}
3327
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003328static int validate_scan_freqs(struct nlattr *freqs)
3329{
3330 struct nlattr *attr1, *attr2;
3331 int n_channels = 0, tmp1, tmp2;
3332
3333 nla_for_each_nested(attr1, freqs, tmp1) {
3334 n_channels++;
3335 /*
3336 * Some hardware has a limited channel list for
3337 * scanning, and it is pretty much nonsensical
3338 * to scan for a channel twice, so disallow that
3339 * and don't require drivers to check that the
3340 * channel list they get isn't longer than what
3341 * they can scan, as long as they can scan all
3342 * the channels they registered at once.
3343 */
3344 nla_for_each_nested(attr2, freqs, tmp2)
3345 if (attr1 != attr2 &&
3346 nla_get_u32(attr1) == nla_get_u32(attr2))
3347 return 0;
3348 }
3349
3350 return n_channels;
3351}
3352
Johannes Berg2a519312009-02-10 21:25:55 +01003353static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3354{
Johannes Berg4c476992010-10-04 21:36:35 +02003355 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3356 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003357 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003358 struct nlattr *attr;
3359 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003360 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003361 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003362
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003363 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3364 return -EINVAL;
3365
Johannes Berg79c97e92009-07-07 03:56:12 +02003366 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003367
Johannes Berg4c476992010-10-04 21:36:35 +02003368 if (!rdev->ops->scan)
3369 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003370
Johannes Berg4c476992010-10-04 21:36:35 +02003371 if (rdev->scan_req)
3372 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003373
3374 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003375 n_channels = validate_scan_freqs(
3376 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003377 if (!n_channels)
3378 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003379 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003380 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003381 n_channels = 0;
3382
Johannes Berg2a519312009-02-10 21:25:55 +01003383 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3384 if (wiphy->bands[band])
3385 n_channels += wiphy->bands[band]->n_channels;
3386 }
3387
3388 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3389 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3390 n_ssids++;
3391
Johannes Berg4c476992010-10-04 21:36:35 +02003392 if (n_ssids > wiphy->max_scan_ssids)
3393 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003394
Jouni Malinen70692ad2009-02-16 19:39:13 +02003395 if (info->attrs[NL80211_ATTR_IE])
3396 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3397 else
3398 ie_len = 0;
3399
Johannes Berg4c476992010-10-04 21:36:35 +02003400 if (ie_len > wiphy->max_scan_ie_len)
3401 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003402
Johannes Berg2a519312009-02-10 21:25:55 +01003403 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003404 + sizeof(*request->ssids) * n_ssids
3405 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003406 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003407 if (!request)
3408 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003409
Johannes Berg2a519312009-02-10 21:25:55 +01003410 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003411 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003412 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003413 if (ie_len) {
3414 if (request->ssids)
3415 request->ie = (void *)(request->ssids + n_ssids);
3416 else
3417 request->ie = (void *)(request->channels + n_channels);
3418 }
Johannes Berg2a519312009-02-10 21:25:55 +01003419
Johannes Berg584991d2009-11-02 13:32:03 +01003420 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003421 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3422 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003423 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003424 struct ieee80211_channel *chan;
3425
3426 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3427
3428 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003429 err = -EINVAL;
3430 goto out_free;
3431 }
Johannes Berg584991d2009-11-02 13:32:03 +01003432
3433 /* ignore disabled channels */
3434 if (chan->flags & IEEE80211_CHAN_DISABLED)
3435 continue;
3436
3437 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003438 i++;
3439 }
3440 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003441 enum ieee80211_band band;
3442
Johannes Berg2a519312009-02-10 21:25:55 +01003443 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003444 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3445 int j;
3446 if (!wiphy->bands[band])
3447 continue;
3448 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003449 struct ieee80211_channel *chan;
3450
3451 chan = &wiphy->bands[band]->channels[j];
3452
3453 if (chan->flags & IEEE80211_CHAN_DISABLED)
3454 continue;
3455
3456 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003457 i++;
3458 }
3459 }
3460 }
3461
Johannes Berg584991d2009-11-02 13:32:03 +01003462 if (!i) {
3463 err = -EINVAL;
3464 goto out_free;
3465 }
3466
3467 request->n_channels = i;
3468
Johannes Berg2a519312009-02-10 21:25:55 +01003469 i = 0;
3470 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3471 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003472 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01003473 err = -EINVAL;
3474 goto out_free;
3475 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003476 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01003477 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01003478 i++;
3479 }
3480 }
3481
Jouni Malinen70692ad2009-02-16 19:39:13 +02003482 if (info->attrs[NL80211_ATTR_IE]) {
3483 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003484 memcpy((void *)request->ie,
3485 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003486 request->ie_len);
3487 }
3488
Johannes Berg34850ab2011-07-18 18:08:35 +02003489 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02003490 if (wiphy->bands[i])
3491 request->rates[i] =
3492 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02003493
3494 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
3495 nla_for_each_nested(attr,
3496 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
3497 tmp) {
3498 enum ieee80211_band band = nla_type(attr);
3499
Dan Carpenter84404622011-07-29 11:52:18 +03003500 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02003501 err = -EINVAL;
3502 goto out_free;
3503 }
3504 err = ieee80211_get_ratemask(wiphy->bands[band],
3505 nla_data(attr),
3506 nla_len(attr),
3507 &request->rates[band]);
3508 if (err)
3509 goto out_free;
3510 }
3511 }
3512
Johannes Berg463d0182009-07-14 00:33:35 +02003513 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003514 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003515
Johannes Berg79c97e92009-07-07 03:56:12 +02003516 rdev->scan_req = request;
3517 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003518
Johannes Berg463d0182009-07-14 00:33:35 +02003519 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003520 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003521 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02003522 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01003523 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02003524 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003525 kfree(request);
3526 }
Johannes Berg3b858752009-03-12 09:55:09 +01003527
Johannes Berg2a519312009-02-10 21:25:55 +01003528 return err;
3529}
3530
Luciano Coelho807f8a82011-05-11 17:09:35 +03003531static int nl80211_start_sched_scan(struct sk_buff *skb,
3532 struct genl_info *info)
3533{
3534 struct cfg80211_sched_scan_request *request;
3535 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3536 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003537 struct nlattr *attr;
3538 struct wiphy *wiphy;
3539 int err, tmp, n_ssids = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003540 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003541 enum ieee80211_band band;
3542 size_t ie_len;
3543
3544 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3545 !rdev->ops->sched_scan_start)
3546 return -EOPNOTSUPP;
3547
3548 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3549 return -EINVAL;
3550
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003551 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
3552 return -EINVAL;
3553
3554 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
3555 if (interval == 0)
3556 return -EINVAL;
3557
Luciano Coelho807f8a82011-05-11 17:09:35 +03003558 wiphy = &rdev->wiphy;
3559
3560 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3561 n_channels = validate_scan_freqs(
3562 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3563 if (!n_channels)
3564 return -EINVAL;
3565 } else {
3566 n_channels = 0;
3567
3568 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3569 if (wiphy->bands[band])
3570 n_channels += wiphy->bands[band]->n_channels;
3571 }
3572
3573 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3574 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3575 tmp)
3576 n_ssids++;
3577
Luciano Coelho93b6aa62011-07-13 14:57:28 +03003578 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003579 return -EINVAL;
3580
3581 if (info->attrs[NL80211_ATTR_IE])
3582 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3583 else
3584 ie_len = 0;
3585
Luciano Coelho5a865ba2011-07-13 14:57:29 +03003586 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003587 return -EINVAL;
3588
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003589 mutex_lock(&rdev->sched_scan_mtx);
3590
3591 if (rdev->sched_scan_req) {
3592 err = -EINPROGRESS;
3593 goto out;
3594 }
3595
Luciano Coelho807f8a82011-05-11 17:09:35 +03003596 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003597 + sizeof(*request->ssids) * n_ssids
3598 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03003599 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003600 if (!request) {
3601 err = -ENOMEM;
3602 goto out;
3603 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03003604
3605 if (n_ssids)
3606 request->ssids = (void *)&request->channels[n_channels];
3607 request->n_ssids = n_ssids;
3608 if (ie_len) {
3609 if (request->ssids)
3610 request->ie = (void *)(request->ssids + n_ssids);
3611 else
3612 request->ie = (void *)(request->channels + n_channels);
3613 }
3614
3615 i = 0;
3616 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3617 /* user specified, bail out if channel not found */
3618 nla_for_each_nested(attr,
3619 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
3620 tmp) {
3621 struct ieee80211_channel *chan;
3622
3623 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3624
3625 if (!chan) {
3626 err = -EINVAL;
3627 goto out_free;
3628 }
3629
3630 /* ignore disabled channels */
3631 if (chan->flags & IEEE80211_CHAN_DISABLED)
3632 continue;
3633
3634 request->channels[i] = chan;
3635 i++;
3636 }
3637 } else {
3638 /* all channels */
3639 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3640 int j;
3641 if (!wiphy->bands[band])
3642 continue;
3643 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3644 struct ieee80211_channel *chan;
3645
3646 chan = &wiphy->bands[band]->channels[j];
3647
3648 if (chan->flags & IEEE80211_CHAN_DISABLED)
3649 continue;
3650
3651 request->channels[i] = chan;
3652 i++;
3653 }
3654 }
3655 }
3656
3657 if (!i) {
3658 err = -EINVAL;
3659 goto out_free;
3660 }
3661
3662 request->n_channels = i;
3663
3664 i = 0;
3665 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3666 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3667 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003668 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03003669 err = -EINVAL;
3670 goto out_free;
3671 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003672 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003673 memcpy(request->ssids[i].ssid, nla_data(attr),
3674 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03003675 i++;
3676 }
3677 }
3678
3679 if (info->attrs[NL80211_ATTR_IE]) {
3680 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3681 memcpy((void *)request->ie,
3682 nla_data(info->attrs[NL80211_ATTR_IE]),
3683 request->ie_len);
3684 }
3685
3686 request->dev = dev;
3687 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003688 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003689
3690 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
3691 if (!err) {
3692 rdev->sched_scan_req = request;
3693 nl80211_send_sched_scan(rdev, dev,
3694 NL80211_CMD_START_SCHED_SCAN);
3695 goto out;
3696 }
3697
3698out_free:
3699 kfree(request);
3700out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003701 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003702 return err;
3703}
3704
3705static int nl80211_stop_sched_scan(struct sk_buff *skb,
3706 struct genl_info *info)
3707{
3708 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003709 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003710
3711 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3712 !rdev->ops->sched_scan_stop)
3713 return -EOPNOTSUPP;
3714
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003715 mutex_lock(&rdev->sched_scan_mtx);
3716 err = __cfg80211_stop_sched_scan(rdev, false);
3717 mutex_unlock(&rdev->sched_scan_mtx);
3718
3719 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003720}
3721
Johannes Berg9720bb32011-06-21 09:45:33 +02003722static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
3723 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01003724 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003725 struct wireless_dev *wdev,
3726 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003727{
Johannes Berg48ab9052009-07-10 18:42:31 +02003728 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003729 void *hdr;
3730 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003731 int i;
3732
3733 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003734
Johannes Berg9720bb32011-06-21 09:45:33 +02003735 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01003736 NL80211_CMD_NEW_SCAN_RESULTS);
3737 if (!hdr)
3738 return -1;
3739
Johannes Berg9720bb32011-06-21 09:45:33 +02003740 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
3741
Johannes Bergf5ea9122009-08-07 16:17:38 +02003742 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003743 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003744
3745 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3746 if (!bss)
3747 goto nla_put_failure;
3748 if (!is_zero_ether_addr(res->bssid))
3749 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3750 if (res->information_elements && res->len_information_elements)
3751 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3752 res->len_information_elements,
3753 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02003754 if (res->beacon_ies && res->len_beacon_ies &&
3755 res->beacon_ies != res->information_elements)
3756 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
3757 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01003758 if (res->tsf)
3759 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3760 if (res->beacon_interval)
3761 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3762 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3763 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003764 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3765 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003766
Johannes Berg77965c92009-02-18 18:45:06 +01003767 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003768 case CFG80211_SIGNAL_TYPE_MBM:
3769 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3770 break;
3771 case CFG80211_SIGNAL_TYPE_UNSPEC:
3772 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3773 break;
3774 default:
3775 break;
3776 }
3777
Johannes Berg48ab9052009-07-10 18:42:31 +02003778 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02003779 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02003780 case NL80211_IFTYPE_STATION:
3781 if (intbss == wdev->current_bss)
3782 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3783 NL80211_BSS_STATUS_ASSOCIATED);
3784 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
3785 if (intbss != wdev->auth_bsses[i])
3786 continue;
3787 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3788 NL80211_BSS_STATUS_AUTHENTICATED);
3789 break;
3790 }
3791 break;
3792 case NL80211_IFTYPE_ADHOC:
3793 if (intbss == wdev->current_bss)
3794 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
3795 NL80211_BSS_STATUS_IBSS_JOINED);
3796 break;
3797 default:
3798 break;
3799 }
3800
Johannes Berg2a519312009-02-10 21:25:55 +01003801 nla_nest_end(msg, bss);
3802
3803 return genlmsg_end(msg, hdr);
3804
3805 nla_put_failure:
3806 genlmsg_cancel(msg, hdr);
3807 return -EMSGSIZE;
3808}
3809
3810static int nl80211_dump_scan(struct sk_buff *skb,
3811 struct netlink_callback *cb)
3812{
Johannes Berg48ab9052009-07-10 18:42:31 +02003813 struct cfg80211_registered_device *rdev;
3814 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01003815 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02003816 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01003817 int start = cb->args[1], idx = 0;
3818 int err;
3819
Johannes Berg67748892010-10-04 21:14:06 +02003820 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
3821 if (err)
3822 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01003823
Johannes Berg48ab9052009-07-10 18:42:31 +02003824 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01003825
Johannes Berg48ab9052009-07-10 18:42:31 +02003826 wdev_lock(wdev);
3827 spin_lock_bh(&rdev->bss_lock);
3828 cfg80211_bss_expire(rdev);
3829
Johannes Berg9720bb32011-06-21 09:45:33 +02003830 cb->seq = rdev->bss_generation;
3831
Johannes Berg48ab9052009-07-10 18:42:31 +02003832 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01003833 if (++idx <= start)
3834 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02003835 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01003836 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02003837 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01003838 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02003839 break;
Johannes Berg2a519312009-02-10 21:25:55 +01003840 }
3841 }
3842
Johannes Berg48ab9052009-07-10 18:42:31 +02003843 spin_unlock_bh(&rdev->bss_lock);
3844 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003845
3846 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02003847 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003848
Johannes Berg67748892010-10-04 21:14:06 +02003849 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01003850}
3851
Holger Schurig61fa7132009-11-11 12:25:40 +01003852static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
3853 int flags, struct net_device *dev,
3854 struct survey_info *survey)
3855{
3856 void *hdr;
3857 struct nlattr *infoattr;
3858
Holger Schurig61fa7132009-11-11 12:25:40 +01003859 hdr = nl80211hdr_put(msg, pid, seq, flags,
3860 NL80211_CMD_NEW_SURVEY_RESULTS);
3861 if (!hdr)
3862 return -ENOMEM;
3863
3864 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3865
3866 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
3867 if (!infoattr)
3868 goto nla_put_failure;
3869
3870 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
3871 survey->channel->center_freq);
3872 if (survey->filled & SURVEY_INFO_NOISE_DBM)
3873 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
3874 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02003875 if (survey->filled & SURVEY_INFO_IN_USE)
3876 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Felix Fietkau8610c292010-10-09 02:39:29 +02003877 if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
3878 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
3879 survey->channel_time);
3880 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
3881 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
3882 survey->channel_time_busy);
3883 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
3884 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
3885 survey->channel_time_ext_busy);
3886 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
3887 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
3888 survey->channel_time_rx);
3889 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
3890 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
3891 survey->channel_time_tx);
Holger Schurig61fa7132009-11-11 12:25:40 +01003892
3893 nla_nest_end(msg, infoattr);
3894
3895 return genlmsg_end(msg, hdr);
3896
3897 nla_put_failure:
3898 genlmsg_cancel(msg, hdr);
3899 return -EMSGSIZE;
3900}
3901
3902static int nl80211_dump_survey(struct sk_buff *skb,
3903 struct netlink_callback *cb)
3904{
3905 struct survey_info survey;
3906 struct cfg80211_registered_device *dev;
3907 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01003908 int survey_idx = cb->args[1];
3909 int res;
3910
Johannes Berg67748892010-10-04 21:14:06 +02003911 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3912 if (res)
3913 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01003914
3915 if (!dev->ops->dump_survey) {
3916 res = -EOPNOTSUPP;
3917 goto out_err;
3918 }
3919
3920 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07003921 struct ieee80211_channel *chan;
3922
Holger Schurig61fa7132009-11-11 12:25:40 +01003923 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
3924 &survey);
3925 if (res == -ENOENT)
3926 break;
3927 if (res)
3928 goto out_err;
3929
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07003930 /* Survey without a channel doesn't make sense */
3931 if (!survey.channel) {
3932 res = -EINVAL;
3933 goto out;
3934 }
3935
3936 chan = ieee80211_get_channel(&dev->wiphy,
3937 survey.channel->center_freq);
3938 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
3939 survey_idx++;
3940 continue;
3941 }
3942
Holger Schurig61fa7132009-11-11 12:25:40 +01003943 if (nl80211_send_survey(skb,
3944 NETLINK_CB(cb->skb).pid,
3945 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3946 netdev,
3947 &survey) < 0)
3948 goto out;
3949 survey_idx++;
3950 }
3951
3952 out:
3953 cb->args[1] = survey_idx;
3954 res = skb->len;
3955 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003956 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01003957 return res;
3958}
3959
Jouni Malinen255e7372009-03-20 21:21:17 +02003960static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
3961{
Samuel Ortizb23aa672009-07-01 21:26:54 +02003962 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02003963}
3964
Samuel Ortizb23aa672009-07-01 21:26:54 +02003965static bool nl80211_valid_wpa_versions(u32 wpa_versions)
3966{
3967 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
3968 NL80211_WPA_VERSION_2));
3969}
3970
3971static bool nl80211_valid_akm_suite(u32 akm)
3972{
3973 return akm == WLAN_AKM_SUITE_8021X ||
3974 akm == WLAN_AKM_SUITE_PSK;
3975}
3976
3977static bool nl80211_valid_cipher_suite(u32 cipher)
3978{
3979 return cipher == WLAN_CIPHER_SUITE_WEP40 ||
3980 cipher == WLAN_CIPHER_SUITE_WEP104 ||
3981 cipher == WLAN_CIPHER_SUITE_TKIP ||
3982 cipher == WLAN_CIPHER_SUITE_CCMP ||
3983 cipher == WLAN_CIPHER_SUITE_AES_CMAC;
3984}
3985
3986
Jouni Malinen636a5d32009-03-19 13:39:22 +02003987static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
3988{
Johannes Berg4c476992010-10-04 21:36:35 +02003989 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3990 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02003991 struct ieee80211_channel *chan;
3992 const u8 *bssid, *ssid, *ie = NULL;
3993 int err, ssid_len, ie_len = 0;
3994 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02003995 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03003996 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02003997
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003998 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3999 return -EINVAL;
4000
4001 if (!info->attrs[NL80211_ATTR_MAC])
4002 return -EINVAL;
4003
Jouni Malinen17780922009-03-27 20:52:47 +02004004 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4005 return -EINVAL;
4006
Johannes Berg19957bb2009-07-02 17:20:43 +02004007 if (!info->attrs[NL80211_ATTR_SSID])
4008 return -EINVAL;
4009
4010 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4011 return -EINVAL;
4012
Johannes Bergfffd0932009-07-08 14:22:54 +02004013 err = nl80211_parse_key(info, &key);
4014 if (err)
4015 return err;
4016
4017 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004018 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4019 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004020 if (!key.p.key || !key.p.key_len)
4021 return -EINVAL;
4022 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4023 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4024 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4025 key.p.key_len != WLAN_KEY_LEN_WEP104))
4026 return -EINVAL;
4027 if (key.idx > 4)
4028 return -EINVAL;
4029 } else {
4030 key.p.key_len = 0;
4031 key.p.key = NULL;
4032 }
4033
Johannes Bergafea0b72010-08-10 09:46:42 +02004034 if (key.idx >= 0) {
4035 int i;
4036 bool ok = false;
4037 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4038 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4039 ok = true;
4040 break;
4041 }
4042 }
Johannes Berg4c476992010-10-04 21:36:35 +02004043 if (!ok)
4044 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004045 }
4046
Johannes Berg4c476992010-10-04 21:36:35 +02004047 if (!rdev->ops->auth)
4048 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004049
Johannes Berg074ac8d2010-09-16 14:58:22 +02004050 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004051 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4052 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004053
Johannes Berg19957bb2009-07-02 17:20:43 +02004054 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004055 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004056 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004057 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4058 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004059
Johannes Berg19957bb2009-07-02 17:20:43 +02004060 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4061 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4062
4063 if (info->attrs[NL80211_ATTR_IE]) {
4064 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4065 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4066 }
4067
4068 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004069 if (!nl80211_valid_auth_type(auth_type))
4070 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004071
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004072 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4073
Johannes Berg4c476992010-10-04 21:36:35 +02004074 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4075 ssid, ssid_len, ie, ie_len,
4076 key.p.key, key.p.key_len, key.idx,
4077 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004078}
4079
Johannes Bergc0692b82010-08-27 14:26:53 +03004080static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4081 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004082 struct cfg80211_crypto_settings *settings,
4083 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004084{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004085 memset(settings, 0, sizeof(*settings));
4086
Samuel Ortizb23aa672009-07-01 21:26:54 +02004087 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4088
Johannes Bergc0692b82010-08-27 14:26:53 +03004089 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4090 u16 proto;
4091 proto = nla_get_u16(
4092 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4093 settings->control_port_ethertype = cpu_to_be16(proto);
4094 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4095 proto != ETH_P_PAE)
4096 return -EINVAL;
4097 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4098 settings->control_port_no_encrypt = true;
4099 } else
4100 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4101
Samuel Ortizb23aa672009-07-01 21:26:54 +02004102 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4103 void *data;
4104 int len, i;
4105
4106 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4107 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4108 settings->n_ciphers_pairwise = len / sizeof(u32);
4109
4110 if (len % sizeof(u32))
4111 return -EINVAL;
4112
Johannes Berg3dc27d22009-07-02 21:36:37 +02004113 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004114 return -EINVAL;
4115
4116 memcpy(settings->ciphers_pairwise, data, len);
4117
4118 for (i = 0; i < settings->n_ciphers_pairwise; i++)
4119 if (!nl80211_valid_cipher_suite(
4120 settings->ciphers_pairwise[i]))
4121 return -EINVAL;
4122 }
4123
4124 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4125 settings->cipher_group =
4126 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
4127 if (!nl80211_valid_cipher_suite(settings->cipher_group))
4128 return -EINVAL;
4129 }
4130
4131 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4132 settings->wpa_versions =
4133 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4134 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4135 return -EINVAL;
4136 }
4137
4138 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4139 void *data;
4140 int len, i;
4141
4142 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4143 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4144 settings->n_akm_suites = len / sizeof(u32);
4145
4146 if (len % sizeof(u32))
4147 return -EINVAL;
4148
4149 memcpy(settings->akm_suites, data, len);
4150
4151 for (i = 0; i < settings->n_ciphers_pairwise; i++)
4152 if (!nl80211_valid_akm_suite(settings->akm_suites[i]))
4153 return -EINVAL;
4154 }
4155
4156 return 0;
4157}
4158
Jouni Malinen636a5d32009-03-19 13:39:22 +02004159static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4160{
Johannes Berg4c476992010-10-04 21:36:35 +02004161 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4162 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004163 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004164 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004165 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004166 int err, ssid_len, ie_len = 0;
4167 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004168
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004169 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4170 return -EINVAL;
4171
4172 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004173 !info->attrs[NL80211_ATTR_SSID] ||
4174 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004175 return -EINVAL;
4176
Johannes Berg4c476992010-10-04 21:36:35 +02004177 if (!rdev->ops->assoc)
4178 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004179
Johannes Berg074ac8d2010-09-16 14:58:22 +02004180 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004181 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4182 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004183
Johannes Berg19957bb2009-07-02 17:20:43 +02004184 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004185
Johannes Berg19957bb2009-07-02 17:20:43 +02004186 chan = ieee80211_get_channel(&rdev->wiphy,
4187 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004188 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4189 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004190
Johannes Berg19957bb2009-07-02 17:20:43 +02004191 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4192 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004193
4194 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004195 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4196 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004197 }
4198
Jouni Malinendc6382ce2009-05-06 22:09:37 +03004199 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004200 enum nl80211_mfp mfp =
Jouni Malinendc6382ce2009-05-06 22:09:37 +03004201 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004202 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004203 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004204 else if (mfp != NL80211_MFP_NO)
4205 return -EINVAL;
Jouni Malinendc6382ce2009-05-06 22:09:37 +03004206 }
4207
Johannes Berg3e5d7642009-07-07 14:37:26 +02004208 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4209 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4210
Johannes Bergc0692b82010-08-27 14:26:53 +03004211 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004212 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004213 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4214 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02004215 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004216
Jouni Malinen636a5d32009-03-19 13:39:22 +02004217 return err;
4218}
4219
4220static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4221{
Johannes Berg4c476992010-10-04 21:36:35 +02004222 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4223 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004224 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004225 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004226 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004227 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004228
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004229 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4230 return -EINVAL;
4231
4232 if (!info->attrs[NL80211_ATTR_MAC])
4233 return -EINVAL;
4234
4235 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4236 return -EINVAL;
4237
Johannes Berg4c476992010-10-04 21:36:35 +02004238 if (!rdev->ops->deauth)
4239 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004240
Johannes Berg074ac8d2010-09-16 14:58:22 +02004241 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004242 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4243 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004244
Johannes Berg19957bb2009-07-02 17:20:43 +02004245 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004246
Johannes Berg19957bb2009-07-02 17:20:43 +02004247 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4248 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004249 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004250 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004251 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004252
4253 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004254 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4255 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004256 }
4257
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004258 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4259
Johannes Berg4c476992010-10-04 21:36:35 +02004260 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4261 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004262}
4263
4264static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4265{
Johannes Berg4c476992010-10-04 21:36:35 +02004266 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4267 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004268 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004269 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004270 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004271 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004272
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004273 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4274 return -EINVAL;
4275
4276 if (!info->attrs[NL80211_ATTR_MAC])
4277 return -EINVAL;
4278
4279 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4280 return -EINVAL;
4281
Johannes Berg4c476992010-10-04 21:36:35 +02004282 if (!rdev->ops->disassoc)
4283 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004284
Johannes Berg074ac8d2010-09-16 14:58:22 +02004285 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004286 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4287 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004288
Johannes Berg19957bb2009-07-02 17:20:43 +02004289 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004290
Johannes Berg19957bb2009-07-02 17:20:43 +02004291 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4292 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004293 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004294 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004295 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004296
4297 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004298 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4299 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004300 }
4301
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004302 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4303
Johannes Berg4c476992010-10-04 21:36:35 +02004304 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4305 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004306}
4307
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004308static bool
4309nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4310 int mcast_rate[IEEE80211_NUM_BANDS],
4311 int rateval)
4312{
4313 struct wiphy *wiphy = &rdev->wiphy;
4314 bool found = false;
4315 int band, i;
4316
4317 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4318 struct ieee80211_supported_band *sband;
4319
4320 sband = wiphy->bands[band];
4321 if (!sband)
4322 continue;
4323
4324 for (i = 0; i < sband->n_bitrates; i++) {
4325 if (sband->bitrates[i].bitrate == rateval) {
4326 mcast_rate[band] = i + 1;
4327 found = true;
4328 break;
4329 }
4330 }
4331 }
4332
4333 return found;
4334}
4335
Johannes Berg04a773a2009-04-19 21:24:32 +02004336static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4337{
Johannes Berg4c476992010-10-04 21:36:35 +02004338 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4339 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004340 struct cfg80211_ibss_params ibss;
4341 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004342 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004343 int err;
4344
Johannes Berg8e30bc52009-04-22 17:45:38 +02004345 memset(&ibss, 0, sizeof(ibss));
4346
Johannes Berg04a773a2009-04-19 21:24:32 +02004347 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4348 return -EINVAL;
4349
4350 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4351 !info->attrs[NL80211_ATTR_SSID] ||
4352 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4353 return -EINVAL;
4354
Johannes Berg8e30bc52009-04-22 17:45:38 +02004355 ibss.beacon_interval = 100;
4356
4357 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4358 ibss.beacon_interval =
4359 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4360 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4361 return -EINVAL;
4362 }
4363
Johannes Berg4c476992010-10-04 21:36:35 +02004364 if (!rdev->ops->join_ibss)
4365 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004366
Johannes Berg4c476992010-10-04 21:36:35 +02004367 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4368 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004369
Johannes Berg79c97e92009-07-07 03:56:12 +02004370 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004371
4372 if (info->attrs[NL80211_ATTR_MAC])
4373 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4374 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4375 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4376
4377 if (info->attrs[NL80211_ATTR_IE]) {
4378 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4379 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4380 }
4381
4382 ibss.channel = ieee80211_get_channel(wiphy,
4383 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4384 if (!ibss.channel ||
4385 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004386 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4387 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004388
4389 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004390 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004391
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004392 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4393 u8 *rates =
4394 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4395 int n_rates =
4396 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4397 struct ieee80211_supported_band *sband =
4398 wiphy->bands[ibss.channel->band];
Johannes Berg34850ab2011-07-18 18:08:35 +02004399 int err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004400
Johannes Berg34850ab2011-07-18 18:08:35 +02004401 err = ieee80211_get_ratemask(sband, rates, n_rates,
4402 &ibss.basic_rates);
4403 if (err)
4404 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004405 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004406
4407 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4408 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4409 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4410 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004411
Johannes Berg4c476992010-10-04 21:36:35 +02004412 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4413 connkeys = nl80211_parse_connkeys(rdev,
4414 info->attrs[NL80211_ATTR_KEYS]);
4415 if (IS_ERR(connkeys))
4416 return PTR_ERR(connkeys);
4417 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004418
Johannes Berg4c476992010-10-04 21:36:35 +02004419 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004420 if (err)
4421 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004422 return err;
4423}
4424
4425static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4426{
Johannes Berg4c476992010-10-04 21:36:35 +02004427 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4428 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004429
Johannes Berg4c476992010-10-04 21:36:35 +02004430 if (!rdev->ops->leave_ibss)
4431 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004432
Johannes Berg4c476992010-10-04 21:36:35 +02004433 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4434 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004435
Johannes Berg4c476992010-10-04 21:36:35 +02004436 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004437}
4438
Johannes Bergaff89a92009-07-01 21:26:51 +02004439#ifdef CONFIG_NL80211_TESTMODE
4440static struct genl_multicast_group nl80211_testmode_mcgrp = {
4441 .name = "testmode",
4442};
4443
4444static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4445{
Johannes Berg4c476992010-10-04 21:36:35 +02004446 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02004447 int err;
4448
4449 if (!info->attrs[NL80211_ATTR_TESTDATA])
4450 return -EINVAL;
4451
Johannes Bergaff89a92009-07-01 21:26:51 +02004452 err = -EOPNOTSUPP;
4453 if (rdev->ops->testmode_cmd) {
4454 rdev->testmode_info = info;
4455 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4456 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4457 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4458 rdev->testmode_info = NULL;
4459 }
4460
Johannes Bergaff89a92009-07-01 21:26:51 +02004461 return err;
4462}
4463
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004464static int nl80211_testmode_dump(struct sk_buff *skb,
4465 struct netlink_callback *cb)
4466{
4467 struct cfg80211_registered_device *dev;
4468 int err;
4469 long phy_idx;
4470 void *data = NULL;
4471 int data_len = 0;
4472
4473 if (cb->args[0]) {
4474 /*
4475 * 0 is a valid index, but not valid for args[0],
4476 * so we need to offset by 1.
4477 */
4478 phy_idx = cb->args[0] - 1;
4479 } else {
4480 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
4481 nl80211_fam.attrbuf, nl80211_fam.maxattr,
4482 nl80211_policy);
4483 if (err)
4484 return err;
4485 if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
4486 return -EINVAL;
4487 phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
4488 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
4489 cb->args[1] =
4490 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
4491 }
4492
4493 if (cb->args[1]) {
4494 data = nla_data((void *)cb->args[1]);
4495 data_len = nla_len((void *)cb->args[1]);
4496 }
4497
4498 mutex_lock(&cfg80211_mutex);
4499 dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
4500 if (!dev) {
4501 mutex_unlock(&cfg80211_mutex);
4502 return -ENOENT;
4503 }
4504 cfg80211_lock_rdev(dev);
4505 mutex_unlock(&cfg80211_mutex);
4506
4507 if (!dev->ops->testmode_dump) {
4508 err = -EOPNOTSUPP;
4509 goto out_err;
4510 }
4511
4512 while (1) {
4513 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
4514 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4515 NL80211_CMD_TESTMODE);
4516 struct nlattr *tmdata;
4517
4518 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
4519 genlmsg_cancel(skb, hdr);
4520 break;
4521 }
4522
4523 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4524 if (!tmdata) {
4525 genlmsg_cancel(skb, hdr);
4526 break;
4527 }
4528 err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
4529 data, data_len);
4530 nla_nest_end(skb, tmdata);
4531
4532 if (err == -ENOBUFS || err == -ENOENT) {
4533 genlmsg_cancel(skb, hdr);
4534 break;
4535 } else if (err) {
4536 genlmsg_cancel(skb, hdr);
4537 goto out_err;
4538 }
4539
4540 genlmsg_end(skb, hdr);
4541 }
4542
4543 err = skb->len;
4544 /* see above */
4545 cb->args[0] = phy_idx + 1;
4546 out_err:
4547 cfg80211_unlock_rdev(dev);
4548 return err;
4549}
4550
Johannes Bergaff89a92009-07-01 21:26:51 +02004551static struct sk_buff *
4552__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
4553 int approxlen, u32 pid, u32 seq, gfp_t gfp)
4554{
4555 struct sk_buff *skb;
4556 void *hdr;
4557 struct nlattr *data;
4558
4559 skb = nlmsg_new(approxlen + 100, gfp);
4560 if (!skb)
4561 return NULL;
4562
4563 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
4564 if (!hdr) {
4565 kfree_skb(skb);
4566 return NULL;
4567 }
4568
4569 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4570 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4571
4572 ((void **)skb->cb)[0] = rdev;
4573 ((void **)skb->cb)[1] = hdr;
4574 ((void **)skb->cb)[2] = data;
4575
4576 return skb;
4577
4578 nla_put_failure:
4579 kfree_skb(skb);
4580 return NULL;
4581}
4582
4583struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
4584 int approxlen)
4585{
4586 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4587
4588 if (WARN_ON(!rdev->testmode_info))
4589 return NULL;
4590
4591 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
4592 rdev->testmode_info->snd_pid,
4593 rdev->testmode_info->snd_seq,
4594 GFP_KERNEL);
4595}
4596EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4597
4598int cfg80211_testmode_reply(struct sk_buff *skb)
4599{
4600 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4601 void *hdr = ((void **)skb->cb)[1];
4602 struct nlattr *data = ((void **)skb->cb)[2];
4603
4604 if (WARN_ON(!rdev->testmode_info)) {
4605 kfree_skb(skb);
4606 return -EINVAL;
4607 }
4608
4609 nla_nest_end(skb, data);
4610 genlmsg_end(skb, hdr);
4611 return genlmsg_reply(skb, rdev->testmode_info);
4612}
4613EXPORT_SYMBOL(cfg80211_testmode_reply);
4614
4615struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4616 int approxlen, gfp_t gfp)
4617{
4618 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4619
4620 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4621}
4622EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4623
4624void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4625{
4626 void *hdr = ((void **)skb->cb)[1];
4627 struct nlattr *data = ((void **)skb->cb)[2];
4628
4629 nla_nest_end(skb, data);
4630 genlmsg_end(skb, hdr);
4631 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4632}
4633EXPORT_SYMBOL(cfg80211_testmode_event);
4634#endif
4635
Samuel Ortizb23aa672009-07-01 21:26:54 +02004636static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4637{
Johannes Berg4c476992010-10-04 21:36:35 +02004638 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4639 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004640 struct cfg80211_connect_params connect;
4641 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004642 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004643 int err;
4644
4645 memset(&connect, 0, sizeof(connect));
4646
4647 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4648 return -EINVAL;
4649
4650 if (!info->attrs[NL80211_ATTR_SSID] ||
4651 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4652 return -EINVAL;
4653
4654 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4655 connect.auth_type =
4656 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4657 if (!nl80211_valid_auth_type(connect.auth_type))
4658 return -EINVAL;
4659 } else
4660 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4661
4662 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4663
Johannes Bergc0692b82010-08-27 14:26:53 +03004664 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004665 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004666 if (err)
4667 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004668
Johannes Berg074ac8d2010-09-16 14:58:22 +02004669 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004670 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4671 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004672
Johannes Berg79c97e92009-07-07 03:56:12 +02004673 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004674
Samuel Ortizb23aa672009-07-01 21:26:54 +02004675 if (info->attrs[NL80211_ATTR_MAC])
4676 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4677 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4678 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4679
4680 if (info->attrs[NL80211_ATTR_IE]) {
4681 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4682 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4683 }
4684
4685 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4686 connect.channel =
4687 ieee80211_get_channel(wiphy,
4688 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4689 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02004690 connect.channel->flags & IEEE80211_CHAN_DISABLED)
4691 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004692 }
4693
Johannes Bergfffd0932009-07-08 14:22:54 +02004694 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4695 connkeys = nl80211_parse_connkeys(rdev,
4696 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02004697 if (IS_ERR(connkeys))
4698 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004699 }
4700
4701 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004702 if (err)
4703 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004704 return err;
4705}
4706
4707static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4708{
Johannes Berg4c476992010-10-04 21:36:35 +02004709 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4710 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004711 u16 reason;
4712
4713 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4714 reason = WLAN_REASON_DEAUTH_LEAVING;
4715 else
4716 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4717
4718 if (reason == 0)
4719 return -EINVAL;
4720
Johannes Berg074ac8d2010-09-16 14:58:22 +02004721 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004722 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4723 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004724
Johannes Berg4c476992010-10-04 21:36:35 +02004725 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004726}
4727
Johannes Berg463d0182009-07-14 00:33:35 +02004728static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4729{
Johannes Berg4c476992010-10-04 21:36:35 +02004730 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02004731 struct net *net;
4732 int err;
4733 u32 pid;
4734
4735 if (!info->attrs[NL80211_ATTR_PID])
4736 return -EINVAL;
4737
4738 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4739
Johannes Berg463d0182009-07-14 00:33:35 +02004740 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02004741 if (IS_ERR(net))
4742 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004743
4744 err = 0;
4745
4746 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02004747 if (!net_eq(wiphy_net(&rdev->wiphy), net))
4748 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02004749
Johannes Berg463d0182009-07-14 00:33:35 +02004750 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004751 return err;
4752}
4753
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004754static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4755{
Johannes Berg4c476992010-10-04 21:36:35 +02004756 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004757 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4758 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02004759 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004760 struct cfg80211_pmksa pmksa;
4761
4762 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4763
4764 if (!info->attrs[NL80211_ATTR_MAC])
4765 return -EINVAL;
4766
4767 if (!info->attrs[NL80211_ATTR_PMKID])
4768 return -EINVAL;
4769
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004770 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4771 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4772
Johannes Berg074ac8d2010-09-16 14:58:22 +02004773 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004774 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4775 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004776
4777 switch (info->genlhdr->cmd) {
4778 case NL80211_CMD_SET_PMKSA:
4779 rdev_ops = rdev->ops->set_pmksa;
4780 break;
4781 case NL80211_CMD_DEL_PMKSA:
4782 rdev_ops = rdev->ops->del_pmksa;
4783 break;
4784 default:
4785 WARN_ON(1);
4786 break;
4787 }
4788
Johannes Berg4c476992010-10-04 21:36:35 +02004789 if (!rdev_ops)
4790 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004791
Johannes Berg4c476992010-10-04 21:36:35 +02004792 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004793}
4794
4795static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
4796{
Johannes Berg4c476992010-10-04 21:36:35 +02004797 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4798 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004799
Johannes Berg074ac8d2010-09-16 14:58:22 +02004800 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004801 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4802 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004803
Johannes Berg4c476992010-10-04 21:36:35 +02004804 if (!rdev->ops->flush_pmksa)
4805 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004806
Johannes Berg4c476992010-10-04 21:36:35 +02004807 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004808}
4809
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004810static int nl80211_remain_on_channel(struct sk_buff *skb,
4811 struct genl_info *info)
4812{
Johannes Berg4c476992010-10-04 21:36:35 +02004813 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4814 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004815 struct ieee80211_channel *chan;
4816 struct sk_buff *msg;
4817 void *hdr;
4818 u64 cookie;
4819 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
4820 u32 freq, duration;
4821 int err;
4822
4823 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4824 !info->attrs[NL80211_ATTR_DURATION])
4825 return -EINVAL;
4826
4827 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
4828
4829 /*
4830 * We should be on that channel for at least one jiffie,
4831 * and more than 5 seconds seems excessive.
4832 */
Johannes Berga2939112010-12-14 17:54:28 +01004833 if (!duration || !msecs_to_jiffies(duration) ||
4834 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004835 return -EINVAL;
4836
Johannes Berg4c476992010-10-04 21:36:35 +02004837 if (!rdev->ops->remain_on_channel)
4838 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004839
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004840 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4841 channel_type = nla_get_u32(
4842 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4843 if (channel_type != NL80211_CHAN_NO_HT &&
4844 channel_type != NL80211_CHAN_HT20 &&
4845 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02004846 channel_type != NL80211_CHAN_HT40MINUS)
4847 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004848 }
4849
4850 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
4851 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02004852 if (chan == NULL)
4853 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004854
4855 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004856 if (!msg)
4857 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004858
4859 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
4860 NL80211_CMD_REMAIN_ON_CHANNEL);
4861
4862 if (IS_ERR(hdr)) {
4863 err = PTR_ERR(hdr);
4864 goto free_msg;
4865 }
4866
4867 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
4868 channel_type, duration, &cookie);
4869
4870 if (err)
4871 goto free_msg;
4872
4873 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
4874
4875 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02004876
4877 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004878
4879 nla_put_failure:
4880 err = -ENOBUFS;
4881 free_msg:
4882 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004883 return err;
4884}
4885
4886static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
4887 struct genl_info *info)
4888{
Johannes Berg4c476992010-10-04 21:36:35 +02004889 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4890 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004891 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004892
4893 if (!info->attrs[NL80211_ATTR_COOKIE])
4894 return -EINVAL;
4895
Johannes Berg4c476992010-10-04 21:36:35 +02004896 if (!rdev->ops->cancel_remain_on_channel)
4897 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004898
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004899 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
4900
Johannes Berg4c476992010-10-04 21:36:35 +02004901 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01004902}
4903
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004904static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
4905 u8 *rates, u8 rates_len)
4906{
4907 u8 i;
4908 u32 mask = 0;
4909
4910 for (i = 0; i < rates_len; i++) {
4911 int rate = (rates[i] & 0x7f) * 5;
4912 int ridx;
4913 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
4914 struct ieee80211_rate *srate =
4915 &sband->bitrates[ridx];
4916 if (rate == srate->bitrate) {
4917 mask |= 1 << ridx;
4918 break;
4919 }
4920 }
4921 if (ridx == sband->n_bitrates)
4922 return 0; /* rate not found */
4923 }
4924
4925 return mask;
4926}
4927
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00004928static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004929 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
4930 .len = NL80211_MAX_SUPP_RATES },
4931};
4932
4933static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
4934 struct genl_info *info)
4935{
4936 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02004937 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004938 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02004939 int rem, i;
4940 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004941 struct nlattr *tx_rates;
4942 struct ieee80211_supported_band *sband;
4943
4944 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
4945 return -EINVAL;
4946
Johannes Berg4c476992010-10-04 21:36:35 +02004947 if (!rdev->ops->set_bitrate_mask)
4948 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004949
4950 memset(&mask, 0, sizeof(mask));
4951 /* Default to all rates enabled */
4952 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
4953 sband = rdev->wiphy.bands[i];
4954 mask.control[i].legacy =
4955 sband ? (1 << sband->n_bitrates) - 1 : 0;
4956 }
4957
4958 /*
4959 * The nested attribute uses enum nl80211_band as the index. This maps
4960 * directly to the enum ieee80211_band values used in cfg80211.
4961 */
4962 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
4963 {
4964 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02004965 if (band < 0 || band >= IEEE80211_NUM_BANDS)
4966 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004967 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02004968 if (sband == NULL)
4969 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004970 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
4971 nla_len(tx_rates), nl80211_txattr_policy);
4972 if (tb[NL80211_TXRATE_LEGACY]) {
4973 mask.control[band].legacy = rateset_to_mask(
4974 sband,
4975 nla_data(tb[NL80211_TXRATE_LEGACY]),
4976 nla_len(tb[NL80211_TXRATE_LEGACY]));
Johannes Berg4c476992010-10-04 21:36:35 +02004977 if (mask.control[band].legacy == 0)
4978 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004979 }
4980 }
4981
Johannes Berg4c476992010-10-04 21:36:35 +02004982 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02004983}
4984
Johannes Berg2e161f72010-08-12 15:38:38 +02004985static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02004986{
Johannes Berg4c476992010-10-04 21:36:35 +02004987 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4988 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02004989 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02004990
4991 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
4992 return -EINVAL;
4993
Johannes Berg2e161f72010-08-12 15:38:38 +02004994 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
4995 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02004996
Johannes Berg9d38d852010-06-09 17:20:33 +02004997 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02004998 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02004999 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5000 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5001 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005002 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005003 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5004 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005005
5006 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005007 if (!rdev->ops->mgmt_tx)
5008 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005009
Johannes Berg4c476992010-10-04 21:36:35 +02005010 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02005011 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005012 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5013 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005014}
5015
Johannes Berg2e161f72010-08-12 15:38:38 +02005016static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005017{
Johannes Berg4c476992010-10-04 21:36:35 +02005018 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5019 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005020 struct ieee80211_channel *chan;
5021 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005022 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005023 u32 freq;
5024 int err;
5025 void *hdr;
5026 u64 cookie;
5027 struct sk_buff *msg;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005028 unsigned int wait = 0;
5029 bool offchan;
Jouni Malinen026331c2010-02-15 12:53:10 +02005030
5031 if (!info->attrs[NL80211_ATTR_FRAME] ||
5032 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5033 return -EINVAL;
5034
Johannes Berg4c476992010-10-04 21:36:35 +02005035 if (!rdev->ops->mgmt_tx)
5036 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005037
Johannes Berg9d38d852010-06-09 17:20:33 +02005038 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005039 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005040 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5041 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5042 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005043 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005044 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5045 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005046
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005047 if (info->attrs[NL80211_ATTR_DURATION]) {
5048 if (!rdev->ops->mgmt_tx_cancel_wait)
5049 return -EINVAL;
5050 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5051 }
5052
Jouni Malinen026331c2010-02-15 12:53:10 +02005053 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5054 channel_type = nla_get_u32(
5055 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5056 if (channel_type != NL80211_CHAN_NO_HT &&
5057 channel_type != NL80211_CHAN_HT20 &&
5058 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005059 channel_type != NL80211_CHAN_HT40MINUS)
5060 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005061 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005062 }
5063
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005064 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5065
Jouni Malinen026331c2010-02-15 12:53:10 +02005066 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5067 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005068 if (chan == NULL)
5069 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005070
5071 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005072 if (!msg)
5073 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005074
5075 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg2e161f72010-08-12 15:38:38 +02005076 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005077
5078 if (IS_ERR(hdr)) {
5079 err = PTR_ERR(hdr);
5080 goto free_msg;
5081 }
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005082 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5083 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02005084 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5085 nla_len(info->attrs[NL80211_ATTR_FRAME]),
5086 &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005087 if (err)
5088 goto free_msg;
5089
5090 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5091
5092 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005093 return genlmsg_reply(msg, info);
Jouni Malinen026331c2010-02-15 12:53:10 +02005094
5095 nla_put_failure:
5096 err = -ENOBUFS;
5097 free_msg:
5098 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005099 return err;
5100}
5101
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005102static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5103{
5104 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5105 struct net_device *dev = info->user_ptr[1];
5106 u64 cookie;
5107
5108 if (!info->attrs[NL80211_ATTR_COOKIE])
5109 return -EINVAL;
5110
5111 if (!rdev->ops->mgmt_tx_cancel_wait)
5112 return -EOPNOTSUPP;
5113
5114 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5115 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5116 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5117 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5118 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5119 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5120 return -EOPNOTSUPP;
5121
5122 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5123
5124 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5125}
5126
Kalle Valoffb9eb32010-02-17 17:58:10 +02005127static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5128{
Johannes Berg4c476992010-10-04 21:36:35 +02005129 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005130 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005131 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005132 u8 ps_state;
5133 bool state;
5134 int err;
5135
Johannes Berg4c476992010-10-04 21:36:35 +02005136 if (!info->attrs[NL80211_ATTR_PS_STATE])
5137 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005138
5139 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5140
Johannes Berg4c476992010-10-04 21:36:35 +02005141 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5142 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005143
5144 wdev = dev->ieee80211_ptr;
5145
Johannes Berg4c476992010-10-04 21:36:35 +02005146 if (!rdev->ops->set_power_mgmt)
5147 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005148
5149 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5150
5151 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005152 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005153
Johannes Berg4c476992010-10-04 21:36:35 +02005154 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5155 wdev->ps_timeout);
5156 if (!err)
5157 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005158 return err;
5159}
5160
5161static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5162{
Johannes Berg4c476992010-10-04 21:36:35 +02005163 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005164 enum nl80211_ps_state ps_state;
5165 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005166 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005167 struct sk_buff *msg;
5168 void *hdr;
5169 int err;
5170
Kalle Valoffb9eb32010-02-17 17:58:10 +02005171 wdev = dev->ieee80211_ptr;
5172
Johannes Berg4c476992010-10-04 21:36:35 +02005173 if (!rdev->ops->set_power_mgmt)
5174 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005175
5176 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005177 if (!msg)
5178 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005179
5180 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5181 NL80211_CMD_GET_POWER_SAVE);
5182 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005183 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005184 goto free_msg;
5185 }
5186
5187 if (wdev->ps)
5188 ps_state = NL80211_PS_ENABLED;
5189 else
5190 ps_state = NL80211_PS_DISABLED;
5191
5192 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5193
5194 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005195 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005196
Johannes Berg4c476992010-10-04 21:36:35 +02005197 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005198 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005199 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005200 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005201 return err;
5202}
5203
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005204static struct nla_policy
5205nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5206 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5207 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5208 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5209};
5210
5211static int nl80211_set_cqm_rssi(struct genl_info *info,
5212 s32 threshold, u32 hysteresis)
5213{
Johannes Berg4c476992010-10-04 21:36:35 +02005214 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005215 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005216 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005217
5218 if (threshold > 0)
5219 return -EINVAL;
5220
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005221 wdev = dev->ieee80211_ptr;
5222
Johannes Berg4c476992010-10-04 21:36:35 +02005223 if (!rdev->ops->set_cqm_rssi_config)
5224 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005225
Johannes Berg074ac8d2010-09-16 14:58:22 +02005226 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005227 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
5228 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005229
Johannes Berg4c476992010-10-04 21:36:35 +02005230 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5231 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005232}
5233
5234static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5235{
5236 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5237 struct nlattr *cqm;
5238 int err;
5239
5240 cqm = info->attrs[NL80211_ATTR_CQM];
5241 if (!cqm) {
5242 err = -EINVAL;
5243 goto out;
5244 }
5245
5246 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5247 nl80211_attr_cqm_policy);
5248 if (err)
5249 goto out;
5250
5251 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5252 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5253 s32 threshold;
5254 u32 hysteresis;
5255 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5256 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5257 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5258 } else
5259 err = -EINVAL;
5260
5261out:
5262 return err;
5263}
5264
Johannes Berg29cbe682010-12-03 09:20:44 +01005265static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
5266{
5267 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5268 struct net_device *dev = info->user_ptr[1];
5269 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08005270 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01005271 int err;
5272
5273 /* start with default */
5274 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08005275 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01005276
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005277 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01005278 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005279 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01005280 if (err)
5281 return err;
5282 }
5283
5284 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
5285 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
5286 return -EINVAL;
5287
Javier Cardonac80d5452010-12-16 17:37:49 -08005288 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
5289 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
5290
5291 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
5292 /* parse additional setup parameters if given */
5293 err = nl80211_parse_mesh_setup(info, &setup);
5294 if (err)
5295 return err;
5296 }
5297
5298 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01005299}
5300
5301static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
5302{
5303 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5304 struct net_device *dev = info->user_ptr[1];
5305
5306 return cfg80211_leave_mesh(rdev, dev);
5307}
5308
Johannes Bergff1b6e62011-05-04 15:37:28 +02005309static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
5310{
5311 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5312 struct sk_buff *msg;
5313 void *hdr;
5314
5315 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5316 return -EOPNOTSUPP;
5317
5318 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5319 if (!msg)
5320 return -ENOMEM;
5321
5322 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5323 NL80211_CMD_GET_WOWLAN);
5324 if (!hdr)
5325 goto nla_put_failure;
5326
5327 if (rdev->wowlan) {
5328 struct nlattr *nl_wowlan;
5329
5330 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
5331 if (!nl_wowlan)
5332 goto nla_put_failure;
5333
5334 if (rdev->wowlan->any)
5335 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
5336 if (rdev->wowlan->disconnect)
5337 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
5338 if (rdev->wowlan->magic_pkt)
5339 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02005340 if (rdev->wowlan->gtk_rekey_failure)
5341 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
5342 if (rdev->wowlan->eap_identity_req)
5343 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
5344 if (rdev->wowlan->four_way_handshake)
5345 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
5346 if (rdev->wowlan->rfkill_release)
5347 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02005348 if (rdev->wowlan->n_patterns) {
5349 struct nlattr *nl_pats, *nl_pat;
5350 int i, pat_len;
5351
5352 nl_pats = nla_nest_start(msg,
5353 NL80211_WOWLAN_TRIG_PKT_PATTERN);
5354 if (!nl_pats)
5355 goto nla_put_failure;
5356
5357 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
5358 nl_pat = nla_nest_start(msg, i + 1);
5359 if (!nl_pat)
5360 goto nla_put_failure;
5361 pat_len = rdev->wowlan->patterns[i].pattern_len;
5362 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
5363 DIV_ROUND_UP(pat_len, 8),
5364 rdev->wowlan->patterns[i].mask);
5365 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
5366 pat_len,
5367 rdev->wowlan->patterns[i].pattern);
5368 nla_nest_end(msg, nl_pat);
5369 }
5370 nla_nest_end(msg, nl_pats);
5371 }
5372
5373 nla_nest_end(msg, nl_wowlan);
5374 }
5375
5376 genlmsg_end(msg, hdr);
5377 return genlmsg_reply(msg, info);
5378
5379nla_put_failure:
5380 nlmsg_free(msg);
5381 return -ENOBUFS;
5382}
5383
5384static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
5385{
5386 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5387 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
5388 struct cfg80211_wowlan no_triggers = {};
5389 struct cfg80211_wowlan new_triggers = {};
5390 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
5391 int err, i;
5392
5393 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5394 return -EOPNOTSUPP;
5395
5396 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
5397 goto no_triggers;
5398
5399 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
5400 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5401 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5402 nl80211_wowlan_policy);
5403 if (err)
5404 return err;
5405
5406 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
5407 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
5408 return -EINVAL;
5409 new_triggers.any = true;
5410 }
5411
5412 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
5413 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
5414 return -EINVAL;
5415 new_triggers.disconnect = true;
5416 }
5417
5418 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
5419 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
5420 return -EINVAL;
5421 new_triggers.magic_pkt = true;
5422 }
5423
Johannes Berg77dbbb12011-07-13 10:48:55 +02005424 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
5425 return -EINVAL;
5426
5427 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
5428 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
5429 return -EINVAL;
5430 new_triggers.gtk_rekey_failure = true;
5431 }
5432
5433 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
5434 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
5435 return -EINVAL;
5436 new_triggers.eap_identity_req = true;
5437 }
5438
5439 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
5440 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
5441 return -EINVAL;
5442 new_triggers.four_way_handshake = true;
5443 }
5444
5445 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
5446 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
5447 return -EINVAL;
5448 new_triggers.rfkill_release = true;
5449 }
5450
Johannes Bergff1b6e62011-05-04 15:37:28 +02005451 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
5452 struct nlattr *pat;
5453 int n_patterns = 0;
5454 int rem, pat_len, mask_len;
5455 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
5456
5457 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5458 rem)
5459 n_patterns++;
5460 if (n_patterns > wowlan->n_patterns)
5461 return -EINVAL;
5462
5463 new_triggers.patterns = kcalloc(n_patterns,
5464 sizeof(new_triggers.patterns[0]),
5465 GFP_KERNEL);
5466 if (!new_triggers.patterns)
5467 return -ENOMEM;
5468
5469 new_triggers.n_patterns = n_patterns;
5470 i = 0;
5471
5472 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5473 rem) {
5474 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
5475 nla_data(pat), nla_len(pat), NULL);
5476 err = -EINVAL;
5477 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
5478 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
5479 goto error;
5480 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
5481 mask_len = DIV_ROUND_UP(pat_len, 8);
5482 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
5483 mask_len)
5484 goto error;
5485 if (pat_len > wowlan->pattern_max_len ||
5486 pat_len < wowlan->pattern_min_len)
5487 goto error;
5488
5489 new_triggers.patterns[i].mask =
5490 kmalloc(mask_len + pat_len, GFP_KERNEL);
5491 if (!new_triggers.patterns[i].mask) {
5492 err = -ENOMEM;
5493 goto error;
5494 }
5495 new_triggers.patterns[i].pattern =
5496 new_triggers.patterns[i].mask + mask_len;
5497 memcpy(new_triggers.patterns[i].mask,
5498 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
5499 mask_len);
5500 new_triggers.patterns[i].pattern_len = pat_len;
5501 memcpy(new_triggers.patterns[i].pattern,
5502 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
5503 pat_len);
5504 i++;
5505 }
5506 }
5507
5508 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
5509 struct cfg80211_wowlan *ntrig;
5510 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
5511 GFP_KERNEL);
5512 if (!ntrig) {
5513 err = -ENOMEM;
5514 goto error;
5515 }
5516 cfg80211_rdev_free_wowlan(rdev);
5517 rdev->wowlan = ntrig;
5518 } else {
5519 no_triggers:
5520 cfg80211_rdev_free_wowlan(rdev);
5521 rdev->wowlan = NULL;
5522 }
5523
5524 return 0;
5525 error:
5526 for (i = 0; i < new_triggers.n_patterns; i++)
5527 kfree(new_triggers.patterns[i].mask);
5528 kfree(new_triggers.patterns);
5529 return err;
5530}
5531
Johannes Berge5497d72011-07-05 16:35:40 +02005532static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
5533{
5534 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5535 struct net_device *dev = info->user_ptr[1];
5536 struct wireless_dev *wdev = dev->ieee80211_ptr;
5537 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
5538 struct cfg80211_gtk_rekey_data rekey_data;
5539 int err;
5540
5541 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
5542 return -EINVAL;
5543
5544 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
5545 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
5546 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
5547 nl80211_rekey_policy);
5548 if (err)
5549 return err;
5550
5551 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
5552 return -ERANGE;
5553 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
5554 return -ERANGE;
5555 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
5556 return -ERANGE;
5557
5558 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
5559 NL80211_KEK_LEN);
5560 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
5561 NL80211_KCK_LEN);
5562 memcpy(rekey_data.replay_ctr,
5563 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
5564 NL80211_REPLAY_CTR_LEN);
5565
5566 wdev_lock(wdev);
5567 if (!wdev->current_bss) {
5568 err = -ENOTCONN;
5569 goto out;
5570 }
5571
5572 if (!rdev->ops->set_rekey_data) {
5573 err = -EOPNOTSUPP;
5574 goto out;
5575 }
5576
5577 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
5578 out:
5579 wdev_unlock(wdev);
5580 return err;
5581}
5582
Johannes Berg4c476992010-10-04 21:36:35 +02005583#define NL80211_FLAG_NEED_WIPHY 0x01
5584#define NL80211_FLAG_NEED_NETDEV 0x02
5585#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02005586#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
5587#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
5588 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02005589
5590static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
5591 struct genl_info *info)
5592{
5593 struct cfg80211_registered_device *rdev;
5594 struct net_device *dev;
5595 int err;
5596 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
5597
5598 if (rtnl)
5599 rtnl_lock();
5600
5601 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
5602 rdev = cfg80211_get_dev_from_info(info);
5603 if (IS_ERR(rdev)) {
5604 if (rtnl)
5605 rtnl_unlock();
5606 return PTR_ERR(rdev);
5607 }
5608 info->user_ptr[0] = rdev;
5609 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
5610 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5611 if (err) {
5612 if (rtnl)
5613 rtnl_unlock();
5614 return err;
5615 }
Johannes Berg41265712010-10-04 21:14:05 +02005616 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
5617 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02005618 cfg80211_unlock_rdev(rdev);
5619 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02005620 if (rtnl)
5621 rtnl_unlock();
5622 return -ENETDOWN;
5623 }
Johannes Berg4c476992010-10-04 21:36:35 +02005624 info->user_ptr[0] = rdev;
5625 info->user_ptr[1] = dev;
5626 }
5627
5628 return 0;
5629}
5630
5631static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
5632 struct genl_info *info)
5633{
5634 if (info->user_ptr[0])
5635 cfg80211_unlock_rdev(info->user_ptr[0]);
5636 if (info->user_ptr[1])
5637 dev_put(info->user_ptr[1]);
5638 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
5639 rtnl_unlock();
5640}
5641
Johannes Berg55682962007-09-20 13:09:35 -04005642static struct genl_ops nl80211_ops[] = {
5643 {
5644 .cmd = NL80211_CMD_GET_WIPHY,
5645 .doit = nl80211_get_wiphy,
5646 .dumpit = nl80211_dump_wiphy,
5647 .policy = nl80211_policy,
5648 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005649 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04005650 },
5651 {
5652 .cmd = NL80211_CMD_SET_WIPHY,
5653 .doit = nl80211_set_wiphy,
5654 .policy = nl80211_policy,
5655 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005656 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005657 },
5658 {
5659 .cmd = NL80211_CMD_GET_INTERFACE,
5660 .doit = nl80211_get_interface,
5661 .dumpit = nl80211_dump_interface,
5662 .policy = nl80211_policy,
5663 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005664 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04005665 },
5666 {
5667 .cmd = NL80211_CMD_SET_INTERFACE,
5668 .doit = nl80211_set_interface,
5669 .policy = nl80211_policy,
5670 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005671 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5672 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005673 },
5674 {
5675 .cmd = NL80211_CMD_NEW_INTERFACE,
5676 .doit = nl80211_new_interface,
5677 .policy = nl80211_policy,
5678 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005679 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5680 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005681 },
5682 {
5683 .cmd = NL80211_CMD_DEL_INTERFACE,
5684 .doit = nl80211_del_interface,
5685 .policy = nl80211_policy,
5686 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005687 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5688 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04005689 },
Johannes Berg41ade002007-12-19 02:03:29 +01005690 {
5691 .cmd = NL80211_CMD_GET_KEY,
5692 .doit = nl80211_get_key,
5693 .policy = nl80211_policy,
5694 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005695 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5696 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005697 },
5698 {
5699 .cmd = NL80211_CMD_SET_KEY,
5700 .doit = nl80211_set_key,
5701 .policy = nl80211_policy,
5702 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005703 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005704 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005705 },
5706 {
5707 .cmd = NL80211_CMD_NEW_KEY,
5708 .doit = nl80211_new_key,
5709 .policy = nl80211_policy,
5710 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005711 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005712 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005713 },
5714 {
5715 .cmd = NL80211_CMD_DEL_KEY,
5716 .doit = nl80211_del_key,
5717 .policy = nl80211_policy,
5718 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005719 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005720 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01005721 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01005722 {
5723 .cmd = NL80211_CMD_SET_BEACON,
5724 .policy = nl80211_policy,
5725 .flags = GENL_ADMIN_PERM,
5726 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005727 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5728 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005729 },
5730 {
5731 .cmd = NL80211_CMD_NEW_BEACON,
5732 .policy = nl80211_policy,
5733 .flags = GENL_ADMIN_PERM,
5734 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005735 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5736 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005737 },
5738 {
5739 .cmd = NL80211_CMD_DEL_BEACON,
5740 .policy = nl80211_policy,
5741 .flags = GENL_ADMIN_PERM,
5742 .doit = nl80211_del_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02005743 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5744 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01005745 },
Johannes Berg5727ef12007-12-19 02:03:34 +01005746 {
5747 .cmd = NL80211_CMD_GET_STATION,
5748 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005749 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01005750 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02005751 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5752 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005753 },
5754 {
5755 .cmd = NL80211_CMD_SET_STATION,
5756 .doit = nl80211_set_station,
5757 .policy = nl80211_policy,
5758 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005759 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5760 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005761 },
5762 {
5763 .cmd = NL80211_CMD_NEW_STATION,
5764 .doit = nl80211_new_station,
5765 .policy = nl80211_policy,
5766 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005767 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005768 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005769 },
5770 {
5771 .cmd = NL80211_CMD_DEL_STATION,
5772 .doit = nl80211_del_station,
5773 .policy = nl80211_policy,
5774 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005775 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5776 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01005777 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005778 {
5779 .cmd = NL80211_CMD_GET_MPATH,
5780 .doit = nl80211_get_mpath,
5781 .dumpit = nl80211_dump_mpath,
5782 .policy = nl80211_policy,
5783 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005784 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005785 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005786 },
5787 {
5788 .cmd = NL80211_CMD_SET_MPATH,
5789 .doit = nl80211_set_mpath,
5790 .policy = nl80211_policy,
5791 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005792 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005793 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005794 },
5795 {
5796 .cmd = NL80211_CMD_NEW_MPATH,
5797 .doit = nl80211_new_mpath,
5798 .policy = nl80211_policy,
5799 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005800 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005801 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005802 },
5803 {
5804 .cmd = NL80211_CMD_DEL_MPATH,
5805 .doit = nl80211_del_mpath,
5806 .policy = nl80211_policy,
5807 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005808 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5809 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01005810 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005811 {
5812 .cmd = NL80211_CMD_SET_BSS,
5813 .doit = nl80211_set_bss,
5814 .policy = nl80211_policy,
5815 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005816 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5817 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03005818 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005819 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08005820 .cmd = NL80211_CMD_GET_REG,
5821 .doit = nl80211_get_reg,
5822 .policy = nl80211_policy,
5823 /* can be retrieved by unprivileged users */
5824 },
5825 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07005826 .cmd = NL80211_CMD_SET_REG,
5827 .doit = nl80211_set_reg,
5828 .policy = nl80211_policy,
5829 .flags = GENL_ADMIN_PERM,
5830 },
5831 {
5832 .cmd = NL80211_CMD_REQ_SET_REG,
5833 .doit = nl80211_req_set_reg,
5834 .policy = nl80211_policy,
5835 .flags = GENL_ADMIN_PERM,
5836 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005837 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005838 .cmd = NL80211_CMD_GET_MESH_CONFIG,
5839 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005840 .policy = nl80211_policy,
5841 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02005842 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5843 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005844 },
5845 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005846 .cmd = NL80211_CMD_SET_MESH_CONFIG,
5847 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005848 .policy = nl80211_policy,
5849 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01005850 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005851 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07005852 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02005853 {
Johannes Berg2a519312009-02-10 21:25:55 +01005854 .cmd = NL80211_CMD_TRIGGER_SCAN,
5855 .doit = nl80211_trigger_scan,
5856 .policy = nl80211_policy,
5857 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005858 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005859 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01005860 },
5861 {
5862 .cmd = NL80211_CMD_GET_SCAN,
5863 .policy = nl80211_policy,
5864 .dumpit = nl80211_dump_scan,
5865 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02005866 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03005867 .cmd = NL80211_CMD_START_SCHED_SCAN,
5868 .doit = nl80211_start_sched_scan,
5869 .policy = nl80211_policy,
5870 .flags = GENL_ADMIN_PERM,
5871 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5872 NL80211_FLAG_NEED_RTNL,
5873 },
5874 {
5875 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
5876 .doit = nl80211_stop_sched_scan,
5877 .policy = nl80211_policy,
5878 .flags = GENL_ADMIN_PERM,
5879 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
5880 NL80211_FLAG_NEED_RTNL,
5881 },
5882 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02005883 .cmd = NL80211_CMD_AUTHENTICATE,
5884 .doit = nl80211_authenticate,
5885 .policy = nl80211_policy,
5886 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005887 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005888 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005889 },
5890 {
5891 .cmd = NL80211_CMD_ASSOCIATE,
5892 .doit = nl80211_associate,
5893 .policy = nl80211_policy,
5894 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005895 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005896 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005897 },
5898 {
5899 .cmd = NL80211_CMD_DEAUTHENTICATE,
5900 .doit = nl80211_deauthenticate,
5901 .policy = nl80211_policy,
5902 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005903 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005904 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005905 },
5906 {
5907 .cmd = NL80211_CMD_DISASSOCIATE,
5908 .doit = nl80211_disassociate,
5909 .policy = nl80211_policy,
5910 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005911 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005912 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02005913 },
Johannes Berg04a773a2009-04-19 21:24:32 +02005914 {
5915 .cmd = NL80211_CMD_JOIN_IBSS,
5916 .doit = nl80211_join_ibss,
5917 .policy = nl80211_policy,
5918 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005919 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005920 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02005921 },
5922 {
5923 .cmd = NL80211_CMD_LEAVE_IBSS,
5924 .doit = nl80211_leave_ibss,
5925 .policy = nl80211_policy,
5926 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005927 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005928 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02005929 },
Johannes Bergaff89a92009-07-01 21:26:51 +02005930#ifdef CONFIG_NL80211_TESTMODE
5931 {
5932 .cmd = NL80211_CMD_TESTMODE,
5933 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005934 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02005935 .policy = nl80211_policy,
5936 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005937 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5938 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02005939 },
5940#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02005941 {
5942 .cmd = NL80211_CMD_CONNECT,
5943 .doit = nl80211_connect,
5944 .policy = nl80211_policy,
5945 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005946 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005947 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005948 },
5949 {
5950 .cmd = NL80211_CMD_DISCONNECT,
5951 .doit = nl80211_disconnect,
5952 .policy = nl80211_policy,
5953 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005954 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02005955 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02005956 },
Johannes Berg463d0182009-07-14 00:33:35 +02005957 {
5958 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
5959 .doit = nl80211_wiphy_netns,
5960 .policy = nl80211_policy,
5961 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005962 .internal_flags = NL80211_FLAG_NEED_WIPHY |
5963 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02005964 },
Holger Schurig61fa7132009-11-11 12:25:40 +01005965 {
5966 .cmd = NL80211_CMD_GET_SURVEY,
5967 .policy = nl80211_policy,
5968 .dumpit = nl80211_dump_survey,
5969 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005970 {
5971 .cmd = NL80211_CMD_SET_PMKSA,
5972 .doit = nl80211_setdel_pmksa,
5973 .policy = nl80211_policy,
5974 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005975 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5976 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005977 },
5978 {
5979 .cmd = NL80211_CMD_DEL_PMKSA,
5980 .doit = nl80211_setdel_pmksa,
5981 .policy = nl80211_policy,
5982 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005983 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5984 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005985 },
5986 {
5987 .cmd = NL80211_CMD_FLUSH_PMKSA,
5988 .doit = nl80211_flush_pmksa,
5989 .policy = nl80211_policy,
5990 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02005991 .internal_flags = NL80211_FLAG_NEED_NETDEV |
5992 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005993 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005994 {
5995 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
5996 .doit = nl80211_remain_on_channel,
5997 .policy = nl80211_policy,
5998 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02005999 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006000 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006001 },
6002 {
6003 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6004 .doit = nl80211_cancel_remain_on_channel,
6005 .policy = nl80211_policy,
6006 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006007 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006008 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006009 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006010 {
6011 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
6012 .doit = nl80211_set_tx_bitrate_mask,
6013 .policy = nl80211_policy,
6014 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006015 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6016 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006017 },
Jouni Malinen026331c2010-02-15 12:53:10 +02006018 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006019 .cmd = NL80211_CMD_REGISTER_FRAME,
6020 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006021 .policy = nl80211_policy,
6022 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006023 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6024 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006025 },
6026 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006027 .cmd = NL80211_CMD_FRAME,
6028 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006029 .policy = nl80211_policy,
6030 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006031 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006032 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006033 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02006034 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006035 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
6036 .doit = nl80211_tx_mgmt_cancel_wait,
6037 .policy = nl80211_policy,
6038 .flags = GENL_ADMIN_PERM,
6039 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6040 NL80211_FLAG_NEED_RTNL,
6041 },
6042 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02006043 .cmd = NL80211_CMD_SET_POWER_SAVE,
6044 .doit = nl80211_set_power_save,
6045 .policy = nl80211_policy,
6046 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006047 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6048 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006049 },
6050 {
6051 .cmd = NL80211_CMD_GET_POWER_SAVE,
6052 .doit = nl80211_get_power_save,
6053 .policy = nl80211_policy,
6054 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006055 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6056 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006057 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006058 {
6059 .cmd = NL80211_CMD_SET_CQM,
6060 .doit = nl80211_set_cqm,
6061 .policy = nl80211_policy,
6062 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006063 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6064 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006065 },
Johannes Bergf444de02010-05-05 15:25:02 +02006066 {
6067 .cmd = NL80211_CMD_SET_CHANNEL,
6068 .doit = nl80211_set_channel,
6069 .policy = nl80211_policy,
6070 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006071 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6072 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02006073 },
Bill Jordane8347eb2010-10-01 13:54:28 -04006074 {
6075 .cmd = NL80211_CMD_SET_WDS_PEER,
6076 .doit = nl80211_set_wds_peer,
6077 .policy = nl80211_policy,
6078 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02006079 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6080 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04006081 },
Johannes Berg29cbe682010-12-03 09:20:44 +01006082 {
6083 .cmd = NL80211_CMD_JOIN_MESH,
6084 .doit = nl80211_join_mesh,
6085 .policy = nl80211_policy,
6086 .flags = GENL_ADMIN_PERM,
6087 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6088 NL80211_FLAG_NEED_RTNL,
6089 },
6090 {
6091 .cmd = NL80211_CMD_LEAVE_MESH,
6092 .doit = nl80211_leave_mesh,
6093 .policy = nl80211_policy,
6094 .flags = GENL_ADMIN_PERM,
6095 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6096 NL80211_FLAG_NEED_RTNL,
6097 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02006098 {
6099 .cmd = NL80211_CMD_GET_WOWLAN,
6100 .doit = nl80211_get_wowlan,
6101 .policy = nl80211_policy,
6102 /* can be retrieved by unprivileged users */
6103 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6104 NL80211_FLAG_NEED_RTNL,
6105 },
6106 {
6107 .cmd = NL80211_CMD_SET_WOWLAN,
6108 .doit = nl80211_set_wowlan,
6109 .policy = nl80211_policy,
6110 .flags = GENL_ADMIN_PERM,
6111 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6112 NL80211_FLAG_NEED_RTNL,
6113 },
Johannes Berge5497d72011-07-05 16:35:40 +02006114 {
6115 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
6116 .doit = nl80211_set_rekey_data,
6117 .policy = nl80211_policy,
6118 .flags = GENL_ADMIN_PERM,
6119 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6120 NL80211_FLAG_NEED_RTNL,
6121 },
Johannes Berg55682962007-09-20 13:09:35 -04006122};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006123
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006124static struct genl_multicast_group nl80211_mlme_mcgrp = {
6125 .name = "mlme",
6126};
Johannes Berg55682962007-09-20 13:09:35 -04006127
6128/* multicast groups */
6129static struct genl_multicast_group nl80211_config_mcgrp = {
6130 .name = "config",
6131};
Johannes Berg2a519312009-02-10 21:25:55 +01006132static struct genl_multicast_group nl80211_scan_mcgrp = {
6133 .name = "scan",
6134};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006135static struct genl_multicast_group nl80211_regulatory_mcgrp = {
6136 .name = "regulatory",
6137};
Johannes Berg55682962007-09-20 13:09:35 -04006138
6139/* notification functions */
6140
6141void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
6142{
6143 struct sk_buff *msg;
6144
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006145 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006146 if (!msg)
6147 return;
6148
6149 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
6150 nlmsg_free(msg);
6151 return;
6152 }
6153
Johannes Berg463d0182009-07-14 00:33:35 +02006154 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6155 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006156}
6157
Johannes Berg362a4152009-05-24 16:43:15 +02006158static int nl80211_add_scan_req(struct sk_buff *msg,
6159 struct cfg80211_registered_device *rdev)
6160{
6161 struct cfg80211_scan_request *req = rdev->scan_req;
6162 struct nlattr *nest;
6163 int i;
6164
Johannes Berg667503d2009-07-07 03:56:11 +02006165 ASSERT_RDEV_LOCK(rdev);
6166
Johannes Berg362a4152009-05-24 16:43:15 +02006167 if (WARN_ON(!req))
6168 return 0;
6169
6170 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
6171 if (!nest)
6172 goto nla_put_failure;
6173 for (i = 0; i < req->n_ssids; i++)
6174 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
6175 nla_nest_end(msg, nest);
6176
6177 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
6178 if (!nest)
6179 goto nla_put_failure;
6180 for (i = 0; i < req->n_channels; i++)
6181 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
6182 nla_nest_end(msg, nest);
6183
6184 if (req->ie)
6185 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
6186
6187 return 0;
6188 nla_put_failure:
6189 return -ENOBUFS;
6190}
6191
Johannes Berga538e2d2009-06-16 19:56:42 +02006192static int nl80211_send_scan_msg(struct sk_buff *msg,
6193 struct cfg80211_registered_device *rdev,
6194 struct net_device *netdev,
6195 u32 pid, u32 seq, int flags,
6196 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01006197{
6198 void *hdr;
6199
6200 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6201 if (!hdr)
6202 return -1;
6203
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05006204 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01006205 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6206
Johannes Berg362a4152009-05-24 16:43:15 +02006207 /* ignore errors and send incomplete event anyway */
6208 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01006209
6210 return genlmsg_end(msg, hdr);
6211
6212 nla_put_failure:
6213 genlmsg_cancel(msg, hdr);
6214 return -EMSGSIZE;
6215}
6216
Luciano Coelho807f8a82011-05-11 17:09:35 +03006217static int
6218nl80211_send_sched_scan_msg(struct sk_buff *msg,
6219 struct cfg80211_registered_device *rdev,
6220 struct net_device *netdev,
6221 u32 pid, u32 seq, int flags, u32 cmd)
6222{
6223 void *hdr;
6224
6225 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6226 if (!hdr)
6227 return -1;
6228
6229 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6230 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6231
6232 return genlmsg_end(msg, hdr);
6233
6234 nla_put_failure:
6235 genlmsg_cancel(msg, hdr);
6236 return -EMSGSIZE;
6237}
6238
Johannes Berga538e2d2009-06-16 19:56:42 +02006239void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
6240 struct net_device *netdev)
6241{
6242 struct sk_buff *msg;
6243
6244 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6245 if (!msg)
6246 return;
6247
6248 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6249 NL80211_CMD_TRIGGER_SCAN) < 0) {
6250 nlmsg_free(msg);
6251 return;
6252 }
6253
Johannes Berg463d0182009-07-14 00:33:35 +02006254 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6255 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02006256}
6257
Johannes Berg2a519312009-02-10 21:25:55 +01006258void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
6259 struct net_device *netdev)
6260{
6261 struct sk_buff *msg;
6262
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006263 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006264 if (!msg)
6265 return;
6266
Johannes Berga538e2d2009-06-16 19:56:42 +02006267 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6268 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006269 nlmsg_free(msg);
6270 return;
6271 }
6272
Johannes Berg463d0182009-07-14 00:33:35 +02006273 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6274 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006275}
6276
6277void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
6278 struct net_device *netdev)
6279{
6280 struct sk_buff *msg;
6281
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006282 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006283 if (!msg)
6284 return;
6285
Johannes Berga538e2d2009-06-16 19:56:42 +02006286 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6287 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006288 nlmsg_free(msg);
6289 return;
6290 }
6291
Johannes Berg463d0182009-07-14 00:33:35 +02006292 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6293 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006294}
6295
Luciano Coelho807f8a82011-05-11 17:09:35 +03006296void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
6297 struct net_device *netdev)
6298{
6299 struct sk_buff *msg;
6300
6301 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6302 if (!msg)
6303 return;
6304
6305 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
6306 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
6307 nlmsg_free(msg);
6308 return;
6309 }
6310
6311 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6312 nl80211_scan_mcgrp.id, GFP_KERNEL);
6313}
6314
6315void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
6316 struct net_device *netdev, u32 cmd)
6317{
6318 struct sk_buff *msg;
6319
6320 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6321 if (!msg)
6322 return;
6323
6324 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
6325 nlmsg_free(msg);
6326 return;
6327 }
6328
6329 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6330 nl80211_scan_mcgrp.id, GFP_KERNEL);
6331}
6332
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006333/*
6334 * This can happen on global regulatory changes or device specific settings
6335 * based on custom world regulatory domains.
6336 */
6337void nl80211_send_reg_change_event(struct regulatory_request *request)
6338{
6339 struct sk_buff *msg;
6340 void *hdr;
6341
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006342 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006343 if (!msg)
6344 return;
6345
6346 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
6347 if (!hdr) {
6348 nlmsg_free(msg);
6349 return;
6350 }
6351
6352 /* Userspace can always count this one always being set */
6353 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
6354
6355 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
6356 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6357 NL80211_REGDOM_TYPE_WORLD);
6358 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
6359 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6360 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
6361 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
6362 request->intersect)
6363 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6364 NL80211_REGDOM_TYPE_INTERSECTION);
6365 else {
6366 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6367 NL80211_REGDOM_TYPE_COUNTRY);
6368 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
6369 }
6370
6371 if (wiphy_idx_valid(request->wiphy_idx))
6372 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
6373
6374 if (genlmsg_end(msg, hdr) < 0) {
6375 nlmsg_free(msg);
6376 return;
6377 }
6378
Johannes Bergbc43b282009-07-25 10:54:13 +02006379 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02006380 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02006381 GFP_ATOMIC);
6382 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006383
6384 return;
6385
6386nla_put_failure:
6387 genlmsg_cancel(msg, hdr);
6388 nlmsg_free(msg);
6389}
6390
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006391static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
6392 struct net_device *netdev,
6393 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006394 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006395{
6396 struct sk_buff *msg;
6397 void *hdr;
6398
Johannes Berge6d6e342009-07-01 21:26:47 +02006399 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006400 if (!msg)
6401 return;
6402
6403 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6404 if (!hdr) {
6405 nlmsg_free(msg);
6406 return;
6407 }
6408
6409 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6410 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6411 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6412
6413 if (genlmsg_end(msg, hdr) < 0) {
6414 nlmsg_free(msg);
6415 return;
6416 }
6417
Johannes Berg463d0182009-07-14 00:33:35 +02006418 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6419 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006420 return;
6421
6422 nla_put_failure:
6423 genlmsg_cancel(msg, hdr);
6424 nlmsg_free(msg);
6425}
6426
6427void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006428 struct net_device *netdev, const u8 *buf,
6429 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006430{
6431 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006432 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006433}
6434
6435void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
6436 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006437 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006438{
Johannes Berge6d6e342009-07-01 21:26:47 +02006439 nl80211_send_mlme_event(rdev, netdev, buf, len,
6440 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006441}
6442
Jouni Malinen53b46b82009-03-27 20:53:56 +02006443void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006444 struct net_device *netdev, const u8 *buf,
6445 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006446{
6447 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006448 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006449}
6450
Jouni Malinen53b46b82009-03-27 20:53:56 +02006451void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
6452 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006453 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006454{
6455 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006456 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006457}
6458
Jouni Malinencf4e5942010-12-16 00:52:40 +02006459void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
6460 struct net_device *netdev, const u8 *buf,
6461 size_t len, gfp_t gfp)
6462{
6463 nl80211_send_mlme_event(rdev, netdev, buf, len,
6464 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
6465}
6466
6467void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
6468 struct net_device *netdev, const u8 *buf,
6469 size_t len, gfp_t gfp)
6470{
6471 nl80211_send_mlme_event(rdev, netdev, buf, len,
6472 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
6473}
6474
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04006475static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
6476 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02006477 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006478{
6479 struct sk_buff *msg;
6480 void *hdr;
6481
Johannes Berge6d6e342009-07-01 21:26:47 +02006482 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006483 if (!msg)
6484 return;
6485
6486 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6487 if (!hdr) {
6488 nlmsg_free(msg);
6489 return;
6490 }
6491
6492 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6493 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6494 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
6495 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6496
6497 if (genlmsg_end(msg, hdr) < 0) {
6498 nlmsg_free(msg);
6499 return;
6500 }
6501
Johannes Berg463d0182009-07-14 00:33:35 +02006502 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6503 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006504 return;
6505
6506 nla_put_failure:
6507 genlmsg_cancel(msg, hdr);
6508 nlmsg_free(msg);
6509}
6510
6511void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006512 struct net_device *netdev, const u8 *addr,
6513 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006514{
6515 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02006516 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006517}
6518
6519void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006520 struct net_device *netdev, const u8 *addr,
6521 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006522{
Johannes Berge6d6e342009-07-01 21:26:47 +02006523 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
6524 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006525}
6526
Samuel Ortizb23aa672009-07-01 21:26:54 +02006527void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
6528 struct net_device *netdev, const u8 *bssid,
6529 const u8 *req_ie, size_t req_ie_len,
6530 const u8 *resp_ie, size_t resp_ie_len,
6531 u16 status, gfp_t gfp)
6532{
6533 struct sk_buff *msg;
6534 void *hdr;
6535
6536 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6537 if (!msg)
6538 return;
6539
6540 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
6541 if (!hdr) {
6542 nlmsg_free(msg);
6543 return;
6544 }
6545
6546 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6547 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6548 if (bssid)
6549 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6550 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
6551 if (req_ie)
6552 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6553 if (resp_ie)
6554 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6555
6556 if (genlmsg_end(msg, hdr) < 0) {
6557 nlmsg_free(msg);
6558 return;
6559 }
6560
Johannes Berg463d0182009-07-14 00:33:35 +02006561 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6562 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006563 return;
6564
6565 nla_put_failure:
6566 genlmsg_cancel(msg, hdr);
6567 nlmsg_free(msg);
6568
6569}
6570
6571void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
6572 struct net_device *netdev, const u8 *bssid,
6573 const u8 *req_ie, size_t req_ie_len,
6574 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
6575{
6576 struct sk_buff *msg;
6577 void *hdr;
6578
6579 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6580 if (!msg)
6581 return;
6582
6583 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
6584 if (!hdr) {
6585 nlmsg_free(msg);
6586 return;
6587 }
6588
6589 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6590 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6591 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6592 if (req_ie)
6593 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6594 if (resp_ie)
6595 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6596
6597 if (genlmsg_end(msg, hdr) < 0) {
6598 nlmsg_free(msg);
6599 return;
6600 }
6601
Johannes Berg463d0182009-07-14 00:33:35 +02006602 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6603 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006604 return;
6605
6606 nla_put_failure:
6607 genlmsg_cancel(msg, hdr);
6608 nlmsg_free(msg);
6609
6610}
6611
6612void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
6613 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02006614 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02006615{
6616 struct sk_buff *msg;
6617 void *hdr;
6618
Johannes Berg667503d2009-07-07 03:56:11 +02006619 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006620 if (!msg)
6621 return;
6622
6623 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
6624 if (!hdr) {
6625 nlmsg_free(msg);
6626 return;
6627 }
6628
6629 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6630 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6631 if (from_ap && reason)
6632 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
6633 if (from_ap)
6634 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
6635 if (ie)
6636 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
6637
6638 if (genlmsg_end(msg, hdr) < 0) {
6639 nlmsg_free(msg);
6640 return;
6641 }
6642
Johannes Berg463d0182009-07-14 00:33:35 +02006643 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6644 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006645 return;
6646
6647 nla_put_failure:
6648 genlmsg_cancel(msg, hdr);
6649 nlmsg_free(msg);
6650
6651}
6652
Johannes Berg04a773a2009-04-19 21:24:32 +02006653void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
6654 struct net_device *netdev, const u8 *bssid,
6655 gfp_t gfp)
6656{
6657 struct sk_buff *msg;
6658 void *hdr;
6659
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006660 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006661 if (!msg)
6662 return;
6663
6664 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
6665 if (!hdr) {
6666 nlmsg_free(msg);
6667 return;
6668 }
6669
6670 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6671 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6672 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6673
6674 if (genlmsg_end(msg, hdr) < 0) {
6675 nlmsg_free(msg);
6676 return;
6677 }
6678
Johannes Berg463d0182009-07-14 00:33:35 +02006679 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6680 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02006681 return;
6682
6683 nla_put_failure:
6684 genlmsg_cancel(msg, hdr);
6685 nlmsg_free(msg);
6686}
6687
Javier Cardonac93b5e72011-04-07 15:08:34 -07006688void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
6689 struct net_device *netdev,
6690 const u8 *macaddr, const u8* ie, u8 ie_len,
6691 gfp_t gfp)
6692{
6693 struct sk_buff *msg;
6694 void *hdr;
6695
6696 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6697 if (!msg)
6698 return;
6699
6700 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
6701 if (!hdr) {
6702 nlmsg_free(msg);
6703 return;
6704 }
6705
6706 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6707 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6708 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
6709 if (ie_len && ie)
6710 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
6711
6712 if (genlmsg_end(msg, hdr) < 0) {
6713 nlmsg_free(msg);
6714 return;
6715 }
6716
6717 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6718 nl80211_mlme_mcgrp.id, gfp);
6719 return;
6720
6721 nla_put_failure:
6722 genlmsg_cancel(msg, hdr);
6723 nlmsg_free(msg);
6724}
6725
Jouni Malinena3b8b052009-03-27 21:59:49 +02006726void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
6727 struct net_device *netdev, const u8 *addr,
6728 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02006729 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02006730{
6731 struct sk_buff *msg;
6732 void *hdr;
6733
Johannes Berge6d6e342009-07-01 21:26:47 +02006734 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006735 if (!msg)
6736 return;
6737
6738 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
6739 if (!hdr) {
6740 nlmsg_free(msg);
6741 return;
6742 }
6743
6744 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6745 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6746 if (addr)
6747 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6748 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03006749 if (key_id != -1)
6750 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006751 if (tsc)
6752 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
6753
6754 if (genlmsg_end(msg, hdr) < 0) {
6755 nlmsg_free(msg);
6756 return;
6757 }
6758
Johannes Berg463d0182009-07-14 00:33:35 +02006759 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6760 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02006761 return;
6762
6763 nla_put_failure:
6764 genlmsg_cancel(msg, hdr);
6765 nlmsg_free(msg);
6766}
6767
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006768void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
6769 struct ieee80211_channel *channel_before,
6770 struct ieee80211_channel *channel_after)
6771{
6772 struct sk_buff *msg;
6773 void *hdr;
6774 struct nlattr *nl_freq;
6775
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006776 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006777 if (!msg)
6778 return;
6779
6780 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
6781 if (!hdr) {
6782 nlmsg_free(msg);
6783 return;
6784 }
6785
6786 /*
6787 * Since we are applying the beacon hint to a wiphy we know its
6788 * wiphy_idx is valid
6789 */
6790 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
6791
6792 /* Before */
6793 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
6794 if (!nl_freq)
6795 goto nla_put_failure;
6796 if (nl80211_msg_put_channel(msg, channel_before))
6797 goto nla_put_failure;
6798 nla_nest_end(msg, nl_freq);
6799
6800 /* After */
6801 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
6802 if (!nl_freq)
6803 goto nla_put_failure;
6804 if (nl80211_msg_put_channel(msg, channel_after))
6805 goto nla_put_failure;
6806 nla_nest_end(msg, nl_freq);
6807
6808 if (genlmsg_end(msg, hdr) < 0) {
6809 nlmsg_free(msg);
6810 return;
6811 }
6812
Johannes Berg463d0182009-07-14 00:33:35 +02006813 rcu_read_lock();
6814 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
6815 GFP_ATOMIC);
6816 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04006817
6818 return;
6819
6820nla_put_failure:
6821 genlmsg_cancel(msg, hdr);
6822 nlmsg_free(msg);
6823}
6824
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006825static void nl80211_send_remain_on_chan_event(
6826 int cmd, struct cfg80211_registered_device *rdev,
6827 struct net_device *netdev, u64 cookie,
6828 struct ieee80211_channel *chan,
6829 enum nl80211_channel_type channel_type,
6830 unsigned int duration, gfp_t gfp)
6831{
6832 struct sk_buff *msg;
6833 void *hdr;
6834
6835 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6836 if (!msg)
6837 return;
6838
6839 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6840 if (!hdr) {
6841 nlmsg_free(msg);
6842 return;
6843 }
6844
6845 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6846 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6847 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
6848 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
6849 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6850
6851 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
6852 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
6853
6854 if (genlmsg_end(msg, hdr) < 0) {
6855 nlmsg_free(msg);
6856 return;
6857 }
6858
6859 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6860 nl80211_mlme_mcgrp.id, gfp);
6861 return;
6862
6863 nla_put_failure:
6864 genlmsg_cancel(msg, hdr);
6865 nlmsg_free(msg);
6866}
6867
6868void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
6869 struct net_device *netdev, u64 cookie,
6870 struct ieee80211_channel *chan,
6871 enum nl80211_channel_type channel_type,
6872 unsigned int duration, gfp_t gfp)
6873{
6874 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
6875 rdev, netdev, cookie, chan,
6876 channel_type, duration, gfp);
6877}
6878
6879void nl80211_send_remain_on_channel_cancel(
6880 struct cfg80211_registered_device *rdev, struct net_device *netdev,
6881 u64 cookie, struct ieee80211_channel *chan,
6882 enum nl80211_channel_type channel_type, gfp_t gfp)
6883{
6884 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6885 rdev, netdev, cookie, chan,
6886 channel_type, 0, gfp);
6887}
6888
Johannes Berg98b62182009-12-23 13:15:44 +01006889void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
6890 struct net_device *dev, const u8 *mac_addr,
6891 struct station_info *sinfo, gfp_t gfp)
6892{
6893 struct sk_buff *msg;
6894
6895 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6896 if (!msg)
6897 return;
6898
6899 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
6900 nlmsg_free(msg);
6901 return;
6902 }
6903
6904 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6905 nl80211_mlme_mcgrp.id, gfp);
6906}
6907
Jouni Malinenec15e682011-03-23 15:29:52 +02006908void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
6909 struct net_device *dev, const u8 *mac_addr,
6910 gfp_t gfp)
6911{
6912 struct sk_buff *msg;
6913 void *hdr;
6914
6915 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6916 if (!msg)
6917 return;
6918
6919 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
6920 if (!hdr) {
6921 nlmsg_free(msg);
6922 return;
6923 }
6924
6925 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
6926 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
6927
6928 if (genlmsg_end(msg, hdr) < 0) {
6929 nlmsg_free(msg);
6930 return;
6931 }
6932
6933 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6934 nl80211_mlme_mcgrp.id, gfp);
6935 return;
6936
6937 nla_put_failure:
6938 genlmsg_cancel(msg, hdr);
6939 nlmsg_free(msg);
6940}
6941
Johannes Berg2e161f72010-08-12 15:38:38 +02006942int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
6943 struct net_device *netdev, u32 nlpid,
6944 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006945{
6946 struct sk_buff *msg;
6947 void *hdr;
6948 int err;
6949
6950 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6951 if (!msg)
6952 return -ENOMEM;
6953
Johannes Berg2e161f72010-08-12 15:38:38 +02006954 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02006955 if (!hdr) {
6956 nlmsg_free(msg);
6957 return -ENOMEM;
6958 }
6959
6960 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6961 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6962 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
6963 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6964
6965 err = genlmsg_end(msg, hdr);
6966 if (err < 0) {
6967 nlmsg_free(msg);
6968 return err;
6969 }
6970
6971 err = genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
6972 if (err < 0)
6973 return err;
6974 return 0;
6975
6976 nla_put_failure:
6977 genlmsg_cancel(msg, hdr);
6978 nlmsg_free(msg);
6979 return -ENOBUFS;
6980}
6981
Johannes Berg2e161f72010-08-12 15:38:38 +02006982void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
6983 struct net_device *netdev, u64 cookie,
6984 const u8 *buf, size_t len, bool ack,
6985 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02006986{
6987 struct sk_buff *msg;
6988 void *hdr;
6989
6990 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
6991 if (!msg)
6992 return;
6993
Johannes Berg2e161f72010-08-12 15:38:38 +02006994 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02006995 if (!hdr) {
6996 nlmsg_free(msg);
6997 return;
6998 }
6999
7000 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7001 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7002 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7003 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7004 if (ack)
7005 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7006
7007 if (genlmsg_end(msg, hdr) < 0) {
7008 nlmsg_free(msg);
7009 return;
7010 }
7011
7012 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
7013 return;
7014
7015 nla_put_failure:
7016 genlmsg_cancel(msg, hdr);
7017 nlmsg_free(msg);
7018}
7019
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007020void
7021nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
7022 struct net_device *netdev,
7023 enum nl80211_cqm_rssi_threshold_event rssi_event,
7024 gfp_t gfp)
7025{
7026 struct sk_buff *msg;
7027 struct nlattr *pinfoattr;
7028 void *hdr;
7029
7030 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7031 if (!msg)
7032 return;
7033
7034 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7035 if (!hdr) {
7036 nlmsg_free(msg);
7037 return;
7038 }
7039
7040 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7041 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7042
7043 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7044 if (!pinfoattr)
7045 goto nla_put_failure;
7046
7047 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
7048 rssi_event);
7049
7050 nla_nest_end(msg, pinfoattr);
7051
7052 if (genlmsg_end(msg, hdr) < 0) {
7053 nlmsg_free(msg);
7054 return;
7055 }
7056
7057 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7058 nl80211_mlme_mcgrp.id, gfp);
7059 return;
7060
7061 nla_put_failure:
7062 genlmsg_cancel(msg, hdr);
7063 nlmsg_free(msg);
7064}
7065
Johannes Berge5497d72011-07-05 16:35:40 +02007066void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
7067 struct net_device *netdev, const u8 *bssid,
7068 const u8 *replay_ctr, gfp_t gfp)
7069{
7070 struct sk_buff *msg;
7071 struct nlattr *rekey_attr;
7072 void *hdr;
7073
7074 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7075 if (!msg)
7076 return;
7077
7078 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
7079 if (!hdr) {
7080 nlmsg_free(msg);
7081 return;
7082 }
7083
7084 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7085 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7086 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7087
7088 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
7089 if (!rekey_attr)
7090 goto nla_put_failure;
7091
7092 NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
7093 NL80211_REPLAY_CTR_LEN, replay_ctr);
7094
7095 nla_nest_end(msg, rekey_attr);
7096
7097 if (genlmsg_end(msg, hdr) < 0) {
7098 nlmsg_free(msg);
7099 return;
7100 }
7101
7102 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7103 nl80211_mlme_mcgrp.id, gfp);
7104 return;
7105
7106 nla_put_failure:
7107 genlmsg_cancel(msg, hdr);
7108 nlmsg_free(msg);
7109}
7110
Johannes Bergc063dbf2010-11-24 08:10:05 +01007111void
7112nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
7113 struct net_device *netdev, const u8 *peer,
7114 u32 num_packets, gfp_t gfp)
7115{
7116 struct sk_buff *msg;
7117 struct nlattr *pinfoattr;
7118 void *hdr;
7119
7120 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7121 if (!msg)
7122 return;
7123
7124 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7125 if (!hdr) {
7126 nlmsg_free(msg);
7127 return;
7128 }
7129
7130 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7131 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7132 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
7133
7134 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7135 if (!pinfoattr)
7136 goto nla_put_failure;
7137
7138 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
7139
7140 nla_nest_end(msg, pinfoattr);
7141
7142 if (genlmsg_end(msg, hdr) < 0) {
7143 nlmsg_free(msg);
7144 return;
7145 }
7146
7147 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7148 nl80211_mlme_mcgrp.id, gfp);
7149 return;
7150
7151 nla_put_failure:
7152 genlmsg_cancel(msg, hdr);
7153 nlmsg_free(msg);
7154}
7155
Jouni Malinen026331c2010-02-15 12:53:10 +02007156static int nl80211_netlink_notify(struct notifier_block * nb,
7157 unsigned long state,
7158 void *_notify)
7159{
7160 struct netlink_notify *notify = _notify;
7161 struct cfg80211_registered_device *rdev;
7162 struct wireless_dev *wdev;
7163
7164 if (state != NETLINK_URELEASE)
7165 return NOTIFY_DONE;
7166
7167 rcu_read_lock();
7168
7169 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list)
7170 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02007171 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Jouni Malinen026331c2010-02-15 12:53:10 +02007172
7173 rcu_read_unlock();
7174
7175 return NOTIFY_DONE;
7176}
7177
7178static struct notifier_block nl80211_netlink_notifier = {
7179 .notifier_call = nl80211_netlink_notify,
7180};
7181
Johannes Berg55682962007-09-20 13:09:35 -04007182/* initialisation/exit functions */
7183
7184int nl80211_init(void)
7185{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00007186 int err;
Johannes Berg55682962007-09-20 13:09:35 -04007187
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00007188 err = genl_register_family_with_ops(&nl80211_fam,
7189 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04007190 if (err)
7191 return err;
7192
Johannes Berg55682962007-09-20 13:09:35 -04007193 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
7194 if (err)
7195 goto err_out;
7196
Johannes Berg2a519312009-02-10 21:25:55 +01007197 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
7198 if (err)
7199 goto err_out;
7200
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007201 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
7202 if (err)
7203 goto err_out;
7204
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007205 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
7206 if (err)
7207 goto err_out;
7208
Johannes Bergaff89a92009-07-01 21:26:51 +02007209#ifdef CONFIG_NL80211_TESTMODE
7210 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
7211 if (err)
7212 goto err_out;
7213#endif
7214
Jouni Malinen026331c2010-02-15 12:53:10 +02007215 err = netlink_register_notifier(&nl80211_netlink_notifier);
7216 if (err)
7217 goto err_out;
7218
Johannes Berg55682962007-09-20 13:09:35 -04007219 return 0;
7220 err_out:
7221 genl_unregister_family(&nl80211_fam);
7222 return err;
7223}
7224
7225void nl80211_exit(void)
7226{
Jouni Malinen026331c2010-02-15 12:53:10 +02007227 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04007228 genl_unregister_family(&nl80211_fam);
7229}