blob: 2d3541c5e05851739db352b3e2d7a6902aa2181e [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Jouni Malinen026331c2010-02-15 12:53:10 +02004 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090010#include <linux/slab.h>
Johannes Berg55682962007-09-20 13:09:35 -040011#include <linux/list.h>
12#include <linux/if_ether.h>
13#include <linux/ieee80211.h>
14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h>
16#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010017#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020018#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040019#include <net/genetlink.h>
20#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020021#include <net/sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040022#include "core.h"
23#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070024#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040025
Jouni Malinen5fb628e2011-08-10 23:54:35 +030026static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type);
27static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
28 struct genl_info *info,
29 struct cfg80211_crypto_settings *settings,
30 int cipher_limit);
31
Johannes Berg4c476992010-10-04 21:36:35 +020032static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
34static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
35 struct genl_info *info);
36
Johannes Berg55682962007-09-20 13:09:35 -040037/* the netlink family */
38static struct genl_family nl80211_fam = {
39 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
40 .name = "nl80211", /* have users key off the name instead */
41 .hdrsize = 0, /* no private header */
42 .version = 1, /* no particular meaning now */
43 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020044 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020045 .pre_doit = nl80211_pre_doit,
46 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040047};
48
Johannes Berg79c97e92009-07-07 03:56:12 +020049/* internal helper: get rdev and dev */
Johannes Berg00918d32011-12-13 17:22:05 +010050static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs,
51 struct cfg80211_registered_device **rdev,
52 struct net_device **dev)
Johannes Berg55682962007-09-20 13:09:35 -040053{
54 int ifindex;
55
Johannes Bergbba95fe2008-07-29 13:22:51 +020056 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040057 return -EINVAL;
58
Johannes Bergbba95fe2008-07-29 13:22:51 +020059 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg00918d32011-12-13 17:22:05 +010060 *dev = dev_get_by_index(netns, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040061 if (!*dev)
62 return -ENODEV;
63
Johannes Berg00918d32011-12-13 17:22:05 +010064 *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020065 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040066 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020067 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040068 }
69
70 return 0;
71}
72
Johannes Berga9455402012-06-15 13:32:49 +020073static struct cfg80211_registered_device *
74__cfg80211_rdev_from_info(struct genl_info *info)
75{
76 int ifindex;
77 struct cfg80211_registered_device *bywiphyidx = NULL, *byifidx = NULL;
78 struct net_device *dev;
79 int err = -EINVAL;
80
81 assert_cfg80211_lock();
82
83 if (info->attrs[NL80211_ATTR_WIPHY]) {
84 bywiphyidx = cfg80211_rdev_by_wiphy_idx(
85 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY]));
86 err = -ENODEV;
87 }
88
89 if (info->attrs[NL80211_ATTR_IFINDEX]) {
90 ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
91 dev = dev_get_by_index(genl_info_net(info), ifindex);
92 if (dev) {
93 if (dev->ieee80211_ptr)
94 byifidx =
95 wiphy_to_dev(dev->ieee80211_ptr->wiphy);
96 dev_put(dev);
97 }
98 err = -ENODEV;
99 }
100
101 if (bywiphyidx && byifidx) {
102 if (bywiphyidx != byifidx)
103 return ERR_PTR(-EINVAL);
104 else
105 return bywiphyidx; /* == byifidx */
106 }
107 if (bywiphyidx)
108 return bywiphyidx;
109
110 if (byifidx)
111 return byifidx;
112
113 return ERR_PTR(err);
114}
115
116/*
117 * This function returns a pointer to the driver
118 * that the genl_info item that is passed refers to.
119 * If successful, it returns non-NULL and also locks
120 * the driver's mutex!
121 *
122 * This means that you need to call cfg80211_unlock_rdev()
123 * before being allowed to acquire &cfg80211_mutex!
124 *
125 * This is necessary because we need to lock the global
126 * mutex to get an item off the list safely, and then
127 * we lock the rdev mutex so it doesn't go away under us.
128 *
129 * We don't want to keep cfg80211_mutex locked
130 * for all the time in order to allow requests on
131 * other interfaces to go through at the same time.
132 *
133 * The result of this can be a PTR_ERR and hence must
134 * be checked with IS_ERR() for errors.
135 */
136static struct cfg80211_registered_device *
137cfg80211_get_dev_from_info(struct genl_info *info)
138{
139 struct cfg80211_registered_device *rdev;
140
141 mutex_lock(&cfg80211_mutex);
142 rdev = __cfg80211_rdev_from_info(info);
143
144 /* if it is not an error we grab the lock on
145 * it to assure it won't be going away while
146 * we operate on it */
147 if (!IS_ERR(rdev))
148 mutex_lock(&rdev->mtx);
149
150 mutex_unlock(&cfg80211_mutex);
151
152 return rdev;
153}
154
Johannes Berg55682962007-09-20 13:09:35 -0400155/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000156static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -0400157 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
158 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -0700159 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +0200160 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +0200161 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +0530162 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200163 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
164 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
165 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
166 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +0100167 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -0400168
169 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
170 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
171 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +0100172
Eliad Pellere007b852011-11-24 18:13:56 +0200173 [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
174 [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +0100175
Johannes Bergb9454e82009-07-08 13:29:08 +0200176 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +0100177 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
178 .len = WLAN_MAX_KEY_LEN },
179 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
180 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
181 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen81962262011-11-02 23:36:31 +0200182 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200183 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100184
185 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
186 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
187 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
188 .len = IEEE80211_MAX_DATA_LEN },
189 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
190 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100191 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
192 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
193 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
194 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
195 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100196 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100197 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200198 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100199 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +0800200 .len = IEEE80211_MAX_MESH_ID_LEN },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100201 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300202
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700203 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
204 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
205
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300206 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
207 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
208 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200209 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
210 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100211 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc902008-08-25 11:58:58 +0300212
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800213 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700214 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700215
Johannes Berg6c739412011-11-03 09:27:01 +0100216 [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200217
218 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
219 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
220 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100221 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
222 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200223
224 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
225 .len = IEEE80211_MAX_SSID_LEN },
226 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
227 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200228 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300229 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300230 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300231 [NL80211_ATTR_STA_FLAGS2] = {
232 .len = sizeof(struct nl80211_sta_flag_update),
233 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300234 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300235 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
236 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200237 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
238 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
239 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200240 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100241 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100242 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
243 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100244 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
245 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200246 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200247 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
248 .len = IEEE80211_MAX_DATA_LEN },
249 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200250 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200251 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300252 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200253 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300254 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
255 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200256 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900257 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
258 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100259 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100260 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100261 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200262 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700263 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300264 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200265 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200266 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300267 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Jouni Malinen9946ecf2011-08-10 23:55:56 +0300268 [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
269 .len = IEEE80211_MAX_DATA_LEN },
270 [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
271 .len = IEEE80211_MAX_DATA_LEN },
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530272 [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300273 [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530274 [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
Arik Nemtsov109086c2011-09-28 14:12:50 +0300275 [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
276 [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
277 [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
278 [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
279 [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
Johannes Berge247bd902011-11-04 11:18:21 +0100280 [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
Arik Nemtsov00f740e2011-11-10 11:28:56 +0200281 [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
282 .len = IEEE80211_MAX_DATA_LEN },
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -0700283 [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
Ben Greear7e7c8922011-11-18 11:31:59 -0800284 [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
285 [NL80211_ATTR_HT_CAPABILITY_MASK] = {
286 .len = NL80211_HT_CAPABILITY_LEN
287 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100288 [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +0530289 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
Bala Shanmugam4486ea92012-03-07 17:27:12 +0530290 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
Johannes Berg55682962007-09-20 13:09:35 -0400291};
292
Johannes Berge31b8212010-10-05 19:39:30 +0200293/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000294static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200295 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200296 [NL80211_KEY_IDX] = { .type = NLA_U8 },
297 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200298 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200299 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
300 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200301 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100302 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
303};
304
305/* policy for the key default flags */
306static const struct nla_policy
307nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
308 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
309 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200310};
311
Johannes Bergff1b6e62011-05-04 15:37:28 +0200312/* policy for WoWLAN attributes */
313static const struct nla_policy
314nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
315 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
316 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
317 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
318 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200319 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
320 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
321 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
322 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200323};
324
Johannes Berge5497d72011-07-05 16:35:40 +0200325/* policy for GTK rekey offload attributes */
326static const struct nla_policy
327nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
328 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
329 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
330 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
331};
332
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300333static const struct nla_policy
334nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
Johannes Berg4a4ab0d2012-06-13 11:17:11 +0200335 [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300336 .len = IEEE80211_MAX_SSID_LEN },
337};
338
Holger Schuriga0438972009-11-11 11:30:02 +0100339/* ifidx get helper */
340static int nl80211_get_ifidx(struct netlink_callback *cb)
341{
342 int res;
343
344 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
345 nl80211_fam.attrbuf, nl80211_fam.maxattr,
346 nl80211_policy);
347 if (res)
348 return res;
349
350 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
351 return -EINVAL;
352
353 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
354 if (!res)
355 return -EINVAL;
356 return res;
357}
358
Johannes Berg67748892010-10-04 21:14:06 +0200359static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
360 struct netlink_callback *cb,
361 struct cfg80211_registered_device **rdev,
362 struct net_device **dev)
363{
364 int ifidx = cb->args[0];
365 int err;
366
367 if (!ifidx)
368 ifidx = nl80211_get_ifidx(cb);
369 if (ifidx < 0)
370 return ifidx;
371
372 cb->args[0] = ifidx;
373
374 rtnl_lock();
375
376 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
377 if (!*dev) {
378 err = -ENODEV;
379 goto out_rtnl;
380 }
381
382 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100383 if (IS_ERR(*rdev)) {
384 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200385 goto out_rtnl;
386 }
387
388 return 0;
389 out_rtnl:
390 rtnl_unlock();
391 return err;
392}
393
394static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
395{
396 cfg80211_unlock_rdev(rdev);
397 rtnl_unlock();
398}
399
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100400/* IE validation */
401static bool is_valid_ie_attr(const struct nlattr *attr)
402{
403 const u8 *pos;
404 int len;
405
406 if (!attr)
407 return true;
408
409 pos = nla_data(attr);
410 len = nla_len(attr);
411
412 while (len) {
413 u8 elemlen;
414
415 if (len < 2)
416 return false;
417 len -= 2;
418
419 elemlen = pos[1];
420 if (elemlen > len)
421 return false;
422
423 len -= elemlen;
424 pos += 2 + elemlen;
425 }
426
427 return true;
428}
429
Johannes Berg55682962007-09-20 13:09:35 -0400430/* message building helper */
431static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
432 int flags, u8 cmd)
433{
434 /* since there is no private header just add the generic one */
435 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
436}
437
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400438static int nl80211_msg_put_channel(struct sk_buff *msg,
439 struct ieee80211_channel *chan)
440{
David S. Miller9360ffd2012-03-29 04:41:26 -0400441 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
442 chan->center_freq))
443 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400444
David S. Miller9360ffd2012-03-29 04:41:26 -0400445 if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
446 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
447 goto nla_put_failure;
448 if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
449 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
450 goto nla_put_failure;
451 if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
452 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
453 goto nla_put_failure;
454 if ((chan->flags & IEEE80211_CHAN_RADAR) &&
455 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
456 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400457
David S. Miller9360ffd2012-03-29 04:41:26 -0400458 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
459 DBM_TO_MBM(chan->max_power)))
460 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400461
462 return 0;
463
464 nla_put_failure:
465 return -ENOBUFS;
466}
467
Johannes Berg55682962007-09-20 13:09:35 -0400468/* netlink command implementations */
469
Johannes Bergb9454e82009-07-08 13:29:08 +0200470struct key_parse {
471 struct key_params p;
472 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200473 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200474 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100475 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200476};
477
478static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
479{
480 struct nlattr *tb[NL80211_KEY_MAX + 1];
481 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
482 nl80211_key_policy);
483 if (err)
484 return err;
485
486 k->def = !!tb[NL80211_KEY_DEFAULT];
487 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
488
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100489 if (k->def) {
490 k->def_uni = true;
491 k->def_multi = true;
492 }
493 if (k->defmgmt)
494 k->def_multi = true;
495
Johannes Bergb9454e82009-07-08 13:29:08 +0200496 if (tb[NL80211_KEY_IDX])
497 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
498
499 if (tb[NL80211_KEY_DATA]) {
500 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
501 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
502 }
503
504 if (tb[NL80211_KEY_SEQ]) {
505 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
506 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
507 }
508
509 if (tb[NL80211_KEY_CIPHER])
510 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
511
Johannes Berge31b8212010-10-05 19:39:30 +0200512 if (tb[NL80211_KEY_TYPE]) {
513 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
514 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
515 return -EINVAL;
516 }
517
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100518 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
519 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
Johannes Berg2da8f412012-01-20 13:52:37 +0100520 err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
521 tb[NL80211_KEY_DEFAULT_TYPES],
522 nl80211_key_default_policy);
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100523 if (err)
524 return err;
525
526 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
527 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
528 }
529
Johannes Bergb9454e82009-07-08 13:29:08 +0200530 return 0;
531}
532
533static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
534{
535 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
536 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
537 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
538 }
539
540 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
541 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
542 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
543 }
544
545 if (info->attrs[NL80211_ATTR_KEY_IDX])
546 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
547
548 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
549 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
550
551 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
552 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
553
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100554 if (k->def) {
555 k->def_uni = true;
556 k->def_multi = true;
557 }
558 if (k->defmgmt)
559 k->def_multi = true;
560
Johannes Berge31b8212010-10-05 19:39:30 +0200561 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
562 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
563 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
564 return -EINVAL;
565 }
566
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100567 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
568 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
569 int err = nla_parse_nested(
570 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
571 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
572 nl80211_key_default_policy);
573 if (err)
574 return err;
575
576 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
577 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
578 }
579
Johannes Bergb9454e82009-07-08 13:29:08 +0200580 return 0;
581}
582
583static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
584{
585 int err;
586
587 memset(k, 0, sizeof(*k));
588 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200589 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200590
591 if (info->attrs[NL80211_ATTR_KEY])
592 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
593 else
594 err = nl80211_parse_key_old(info, k);
595
596 if (err)
597 return err;
598
599 if (k->def && k->defmgmt)
600 return -EINVAL;
601
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100602 if (k->defmgmt) {
603 if (k->def_uni || !k->def_multi)
604 return -EINVAL;
605 }
606
Johannes Bergb9454e82009-07-08 13:29:08 +0200607 if (k->idx != -1) {
608 if (k->defmgmt) {
609 if (k->idx < 4 || k->idx > 5)
610 return -EINVAL;
611 } else if (k->def) {
612 if (k->idx < 0 || k->idx > 3)
613 return -EINVAL;
614 } else {
615 if (k->idx < 0 || k->idx > 5)
616 return -EINVAL;
617 }
618 }
619
620 return 0;
621}
622
Johannes Bergfffd0932009-07-08 14:22:54 +0200623static struct cfg80211_cached_keys *
624nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
625 struct nlattr *keys)
626{
627 struct key_parse parse;
628 struct nlattr *key;
629 struct cfg80211_cached_keys *result;
630 int rem, err, def = 0;
631
632 result = kzalloc(sizeof(*result), GFP_KERNEL);
633 if (!result)
634 return ERR_PTR(-ENOMEM);
635
636 result->def = -1;
637 result->defmgmt = -1;
638
639 nla_for_each_nested(key, keys, rem) {
640 memset(&parse, 0, sizeof(parse));
641 parse.idx = -1;
642
643 err = nl80211_parse_key_new(key, &parse);
644 if (err)
645 goto error;
646 err = -EINVAL;
647 if (!parse.p.key)
648 goto error;
649 if (parse.idx < 0 || parse.idx > 4)
650 goto error;
651 if (parse.def) {
652 if (def)
653 goto error;
654 def = 1;
655 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100656 if (!parse.def_uni || !parse.def_multi)
657 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200658 } else if (parse.defmgmt)
659 goto error;
660 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200661 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200662 if (err)
663 goto error;
664 result->params[parse.idx].cipher = parse.p.cipher;
665 result->params[parse.idx].key_len = parse.p.key_len;
666 result->params[parse.idx].key = result->data[parse.idx];
667 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
668 }
669
670 return result;
671 error:
672 kfree(result);
673 return ERR_PTR(err);
674}
675
676static int nl80211_key_allowed(struct wireless_dev *wdev)
677{
678 ASSERT_WDEV_LOCK(wdev);
679
Johannes Bergfffd0932009-07-08 14:22:54 +0200680 switch (wdev->iftype) {
681 case NL80211_IFTYPE_AP:
682 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200683 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700684 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200685 break;
686 case NL80211_IFTYPE_ADHOC:
687 if (!wdev->current_bss)
688 return -ENOLINK;
689 break;
690 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200691 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200692 if (wdev->sme_state != CFG80211_SME_CONNECTED)
693 return -ENOLINK;
694 break;
695 default:
696 return -EINVAL;
697 }
698
699 return 0;
700}
701
Johannes Berg7527a782011-05-13 10:58:57 +0200702static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
703{
704 struct nlattr *nl_modes = nla_nest_start(msg, attr);
705 int i;
706
707 if (!nl_modes)
708 goto nla_put_failure;
709
710 i = 0;
711 while (ifmodes) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400712 if ((ifmodes & 1) && nla_put_flag(msg, i))
713 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200714 ifmodes >>= 1;
715 i++;
716 }
717
718 nla_nest_end(msg, nl_modes);
719 return 0;
720
721nla_put_failure:
722 return -ENOBUFS;
723}
724
725static int nl80211_put_iface_combinations(struct wiphy *wiphy,
726 struct sk_buff *msg)
727{
728 struct nlattr *nl_combis;
729 int i, j;
730
731 nl_combis = nla_nest_start(msg,
732 NL80211_ATTR_INTERFACE_COMBINATIONS);
733 if (!nl_combis)
734 goto nla_put_failure;
735
736 for (i = 0; i < wiphy->n_iface_combinations; i++) {
737 const struct ieee80211_iface_combination *c;
738 struct nlattr *nl_combi, *nl_limits;
739
740 c = &wiphy->iface_combinations[i];
741
742 nl_combi = nla_nest_start(msg, i + 1);
743 if (!nl_combi)
744 goto nla_put_failure;
745
746 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
747 if (!nl_limits)
748 goto nla_put_failure;
749
750 for (j = 0; j < c->n_limits; j++) {
751 struct nlattr *nl_limit;
752
753 nl_limit = nla_nest_start(msg, j + 1);
754 if (!nl_limit)
755 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -0400756 if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
757 c->limits[j].max))
758 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200759 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
760 c->limits[j].types))
761 goto nla_put_failure;
762 nla_nest_end(msg, nl_limit);
763 }
764
765 nla_nest_end(msg, nl_limits);
766
David S. Miller9360ffd2012-03-29 04:41:26 -0400767 if (c->beacon_int_infra_match &&
768 nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
769 goto nla_put_failure;
770 if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
771 c->num_different_channels) ||
772 nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
773 c->max_interfaces))
774 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200775
776 nla_nest_end(msg, nl_combi);
777 }
778
779 nla_nest_end(msg, nl_combis);
780
781 return 0;
782nla_put_failure:
783 return -ENOBUFS;
784}
785
Johannes Berg55682962007-09-20 13:09:35 -0400786static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
787 struct cfg80211_registered_device *dev)
788{
789 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100790 struct nlattr *nl_bands, *nl_band;
791 struct nlattr *nl_freqs, *nl_freq;
792 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100793 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100794 enum ieee80211_band band;
795 struct ieee80211_channel *chan;
796 struct ieee80211_rate *rate;
797 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200798 const struct ieee80211_txrx_stypes *mgmt_stypes =
799 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400800
801 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
802 if (!hdr)
803 return -1;
804
David S. Miller9360ffd2012-03-29 04:41:26 -0400805 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
806 nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
807 nla_put_u32(msg, NL80211_ATTR_GENERATION,
808 cfg80211_rdev_list_generation) ||
809 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
810 dev->wiphy.retry_short) ||
811 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
812 dev->wiphy.retry_long) ||
813 nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
814 dev->wiphy.frag_threshold) ||
815 nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
816 dev->wiphy.rts_threshold) ||
817 nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
818 dev->wiphy.coverage_class) ||
819 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
820 dev->wiphy.max_scan_ssids) ||
821 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
822 dev->wiphy.max_sched_scan_ssids) ||
823 nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
824 dev->wiphy.max_scan_ie_len) ||
825 nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
826 dev->wiphy.max_sched_scan_ie_len) ||
827 nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
828 dev->wiphy.max_match_sets))
829 goto nla_put_failure;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200830
David S. Miller9360ffd2012-03-29 04:41:26 -0400831 if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
832 nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
833 goto nla_put_failure;
834 if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
835 nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
836 goto nla_put_failure;
837 if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
838 nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
839 goto nla_put_failure;
840 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
841 nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
842 goto nla_put_failure;
843 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
844 nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
845 goto nla_put_failure;
846 if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
847 nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
848 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +0200849
David S. Miller9360ffd2012-03-29 04:41:26 -0400850 if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
851 sizeof(u32) * dev->wiphy.n_cipher_suites,
852 dev->wiphy.cipher_suites))
853 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +0100854
David S. Miller9360ffd2012-03-29 04:41:26 -0400855 if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
856 dev->wiphy.max_num_pmkids))
857 goto nla_put_failure;
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530858
David S. Miller9360ffd2012-03-29 04:41:26 -0400859 if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
860 nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
861 goto nla_put_failure;
Johannes Berg25e47c12009-04-02 20:14:06 +0200862
David S. Miller9360ffd2012-03-29 04:41:26 -0400863 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
864 dev->wiphy.available_antennas_tx) ||
865 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
866 dev->wiphy.available_antennas_rx))
867 goto nla_put_failure;
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100868
David S. Miller9360ffd2012-03-29 04:41:26 -0400869 if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
870 nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
871 dev->wiphy.probe_resp_offload))
872 goto nla_put_failure;
Arik Nemtsov87bbbe22011-11-10 11:28:55 +0200873
Bruno Randolf7f531e02010-12-16 11:30:22 +0900874 if ((dev->wiphy.available_antennas_tx ||
875 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900876 u32 tx_ant = 0, rx_ant = 0;
877 int res;
878 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
879 if (!res) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400880 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
881 tx_ant) ||
882 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
883 rx_ant))
884 goto nla_put_failure;
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900885 }
886 }
887
Johannes Berg7527a782011-05-13 10:58:57 +0200888 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
889 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700890 goto nla_put_failure;
891
Johannes Bergee688b002008-01-24 19:38:39 +0100892 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
893 if (!nl_bands)
894 goto nla_put_failure;
895
896 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
897 if (!dev->wiphy.bands[band])
898 continue;
899
900 nl_band = nla_nest_start(msg, band);
901 if (!nl_band)
902 goto nla_put_failure;
903
Johannes Bergd51626d2008-10-09 12:20:13 +0200904 /* add HT info */
David S. Miller9360ffd2012-03-29 04:41:26 -0400905 if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
906 (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
907 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
908 &dev->wiphy.bands[band]->ht_cap.mcs) ||
909 nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
910 dev->wiphy.bands[band]->ht_cap.cap) ||
911 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
912 dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
913 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
914 dev->wiphy.bands[band]->ht_cap.ampdu_density)))
915 goto nla_put_failure;
Johannes Bergd51626d2008-10-09 12:20:13 +0200916
Johannes Bergee688b002008-01-24 19:38:39 +0100917 /* add frequencies */
918 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
919 if (!nl_freqs)
920 goto nla_put_failure;
921
922 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
923 nl_freq = nla_nest_start(msg, i);
924 if (!nl_freq)
925 goto nla_put_failure;
926
927 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100928
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400929 if (nl80211_msg_put_channel(msg, chan))
930 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200931
Johannes Bergee688b002008-01-24 19:38:39 +0100932 nla_nest_end(msg, nl_freq);
933 }
934
935 nla_nest_end(msg, nl_freqs);
936
937 /* add bitrates */
938 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
939 if (!nl_rates)
940 goto nla_put_failure;
941
942 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
943 nl_rate = nla_nest_start(msg, i);
944 if (!nl_rate)
945 goto nla_put_failure;
946
947 rate = &dev->wiphy.bands[band]->bitrates[i];
David S. Miller9360ffd2012-03-29 04:41:26 -0400948 if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
949 rate->bitrate))
950 goto nla_put_failure;
951 if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
952 nla_put_flag(msg,
953 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
954 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +0100955
956 nla_nest_end(msg, nl_rate);
957 }
958
959 nla_nest_end(msg, nl_rates);
960
961 nla_nest_end(msg, nl_band);
962 }
963 nla_nest_end(msg, nl_bands);
964
Johannes Berg8fdc6212009-03-14 09:34:01 +0100965 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
966 if (!nl_cmds)
967 goto nla_put_failure;
968
969 i = 0;
970#define CMD(op, n) \
971 do { \
972 if (dev->ops->op) { \
973 i++; \
David S. Miller9360ffd2012-03-29 04:41:26 -0400974 if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
975 goto nla_put_failure; \
Johannes Berg8fdc6212009-03-14 09:34:01 +0100976 } \
977 } while (0)
978
979 CMD(add_virtual_intf, NEW_INTERFACE);
980 CMD(change_virtual_intf, SET_INTERFACE);
981 CMD(add_key, NEW_KEY);
Johannes Berg88600202012-02-13 15:17:18 +0100982 CMD(start_ap, START_AP);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100983 CMD(add_station, NEW_STATION);
984 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800985 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100986 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200987 CMD(auth, AUTHENTICATE);
988 CMD(assoc, ASSOCIATE);
989 CMD(deauth, DEAUTHENTICATE);
990 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200991 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100992 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100993 CMD(set_pmksa, SET_PMKSA);
994 CMD(del_pmksa, DEL_PMKSA);
995 CMD(flush_pmksa, FLUSH_PMKSA);
Johannes Berg7c4ef712011-11-18 15:33:48 +0100996 if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
997 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200998 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200999 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +01001000 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +01001001 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +02001002 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001003 if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
1004 goto nla_put_failure;
Johannes Berg463d0182009-07-14 00:33:35 +02001005 }
Johannes Berge8c9bd52012-06-06 08:18:22 +02001006 if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
Johannes Bergcc1d2802012-05-16 23:50:20 +02001007 dev->ops->join_mesh) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001008 i++;
1009 if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
1010 goto nla_put_failure;
1011 }
Bill Jordane8347eb2010-10-01 13:54:28 -04001012 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +03001013 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
1014 CMD(tdls_mgmt, TDLS_MGMT);
1015 CMD(tdls_oper, TDLS_OPER);
1016 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03001017 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
1018 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +01001019 CMD(probe_client, PROBE_CLIENT);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01001020 CMD(set_noack_map, SET_NOACK_MAP);
Johannes Berg5e7602302011-11-04 11:18:17 +01001021 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
1022 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001023 if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
1024 goto nla_put_failure;
Johannes Berg5e7602302011-11-04 11:18:17 +01001025 }
Johannes Berg8fdc6212009-03-14 09:34:01 +01001026
Kalle Valo4745fc02011-11-17 19:06:10 +02001027#ifdef CONFIG_NL80211_TESTMODE
1028 CMD(testmode_cmd, TESTMODE);
1029#endif
1030
Johannes Berg8fdc6212009-03-14 09:34:01 +01001031#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +02001032
Johannes Berg6829c872009-07-02 09:13:27 +02001033 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +02001034 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001035 if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
1036 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02001037 }
1038
Johannes Berg6829c872009-07-02 09:13:27 +02001039 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +02001040 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -04001041 if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
1042 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02001043 }
1044
Johannes Berg8fdc6212009-03-14 09:34:01 +01001045 nla_nest_end(msg, nl_cmds);
1046
Johannes Berg7c4ef712011-11-18 15:33:48 +01001047 if (dev->ops->remain_on_channel &&
David S. Miller9360ffd2012-03-29 04:41:26 -04001048 (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
1049 nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
1050 dev->wiphy.max_remain_on_channel_duration))
1051 goto nla_put_failure;
Johannes Berga2939112010-12-14 17:54:28 +01001052
David S. Miller9360ffd2012-03-29 04:41:26 -04001053 if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
1054 nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
1055 goto nla_put_failure;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01001056
Johannes Berg2e161f72010-08-12 15:38:38 +02001057 if (mgmt_stypes) {
1058 u16 stypes;
1059 struct nlattr *nl_ftypes, *nl_ifs;
1060 enum nl80211_iftype ift;
1061
1062 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
1063 if (!nl_ifs)
1064 goto nla_put_failure;
1065
1066 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1067 nl_ftypes = nla_nest_start(msg, ift);
1068 if (!nl_ftypes)
1069 goto nla_put_failure;
1070 i = 0;
1071 stypes = mgmt_stypes[ift].tx;
1072 while (stypes) {
David S. Miller9360ffd2012-03-29 04:41:26 -04001073 if ((stypes & 1) &&
1074 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1075 (i << 4) | IEEE80211_FTYPE_MGMT))
1076 goto nla_put_failure;
Johannes Berg2e161f72010-08-12 15:38:38 +02001077 stypes >>= 1;
1078 i++;
1079 }
1080 nla_nest_end(msg, nl_ftypes);
1081 }
1082
Johannes Berg74b70a42010-08-24 12:15:53 +02001083 nla_nest_end(msg, nl_ifs);
1084
Johannes Berg2e161f72010-08-12 15:38:38 +02001085 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
1086 if (!nl_ifs)
1087 goto nla_put_failure;
1088
1089 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1090 nl_ftypes = nla_nest_start(msg, ift);
1091 if (!nl_ftypes)
1092 goto nla_put_failure;
1093 i = 0;
1094 stypes = mgmt_stypes[ift].rx;
1095 while (stypes) {
David S. Miller9360ffd2012-03-29 04:41:26 -04001096 if ((stypes & 1) &&
1097 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1098 (i << 4) | IEEE80211_FTYPE_MGMT))
1099 goto nla_put_failure;
Johannes Berg2e161f72010-08-12 15:38:38 +02001100 stypes >>= 1;
1101 i++;
1102 }
1103 nla_nest_end(msg, nl_ftypes);
1104 }
1105 nla_nest_end(msg, nl_ifs);
1106 }
1107
Johannes Bergff1b6e62011-05-04 15:37:28 +02001108 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
1109 struct nlattr *nl_wowlan;
1110
1111 nl_wowlan = nla_nest_start(msg,
1112 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
1113 if (!nl_wowlan)
1114 goto nla_put_failure;
1115
David S. Miller9360ffd2012-03-29 04:41:26 -04001116 if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
1117 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
1118 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
1119 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
1120 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
1121 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
1122 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
1123 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
1124 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
1125 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
1126 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
1127 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
1128 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
1129 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
1130 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
1131 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
1132 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001133 if (dev->wiphy.wowlan.n_patterns) {
1134 struct nl80211_wowlan_pattern_support pat = {
1135 .max_patterns = dev->wiphy.wowlan.n_patterns,
1136 .min_pattern_len =
1137 dev->wiphy.wowlan.pattern_min_len,
1138 .max_pattern_len =
1139 dev->wiphy.wowlan.pattern_max_len,
1140 };
David S. Miller9360ffd2012-03-29 04:41:26 -04001141 if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1142 sizeof(pat), &pat))
1143 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001144 }
1145
1146 nla_nest_end(msg, nl_wowlan);
1147 }
1148
Johannes Berg7527a782011-05-13 10:58:57 +02001149 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1150 dev->wiphy.software_iftypes))
1151 goto nla_put_failure;
1152
1153 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1154 goto nla_put_failure;
1155
David S. Miller9360ffd2012-03-29 04:41:26 -04001156 if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
1157 nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
1158 dev->wiphy.ap_sme_capa))
1159 goto nla_put_failure;
Johannes Berg562a7482011-11-07 12:39:33 +01001160
David S. Miller9360ffd2012-03-29 04:41:26 -04001161 if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
1162 dev->wiphy.features))
1163 goto nla_put_failure;
Johannes Berg1f074bd2011-11-06 14:13:33 +01001164
David S. Miller9360ffd2012-03-29 04:41:26 -04001165 if (dev->wiphy.ht_capa_mod_mask &&
1166 nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
1167 sizeof(*dev->wiphy.ht_capa_mod_mask),
1168 dev->wiphy.ht_capa_mod_mask))
1169 goto nla_put_failure;
Ben Greear7e7c8922011-11-18 11:31:59 -08001170
Johannes Berg55682962007-09-20 13:09:35 -04001171 return genlmsg_end(msg, hdr);
1172
1173 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001174 genlmsg_cancel(msg, hdr);
1175 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001176}
1177
1178static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1179{
1180 int idx = 0;
1181 int start = cb->args[0];
1182 struct cfg80211_registered_device *dev;
1183
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001184 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001185 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001186 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1187 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001188 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001189 continue;
1190 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
1191 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001192 dev) < 0) {
1193 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001194 break;
Julius Volzb4637272008-07-08 14:02:19 +02001195 }
Johannes Berg55682962007-09-20 13:09:35 -04001196 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001197 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001198
1199 cb->args[0] = idx;
1200
1201 return skb->len;
1202}
1203
1204static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1205{
1206 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001207 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001208
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001209 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001210 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001211 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001212
Johannes Berg4c476992010-10-04 21:36:35 +02001213 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1214 nlmsg_free(msg);
1215 return -ENOBUFS;
1216 }
Johannes Berg55682962007-09-20 13:09:35 -04001217
Johannes Berg134e6372009-07-10 09:51:34 +00001218 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001219}
1220
Jouni Malinen31888482008-10-30 16:59:24 +02001221static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1222 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1223 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1224 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1225 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1226 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1227};
1228
1229static int parse_txq_params(struct nlattr *tb[],
1230 struct ieee80211_txq_params *txq_params)
1231{
Johannes Berga3304b02012-03-28 11:04:24 +02001232 if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
Jouni Malinen31888482008-10-30 16:59:24 +02001233 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1234 !tb[NL80211_TXQ_ATTR_AIFS])
1235 return -EINVAL;
1236
Johannes Berga3304b02012-03-28 11:04:24 +02001237 txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
Jouni Malinen31888482008-10-30 16:59:24 +02001238 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1239 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1240 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1241 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1242
Johannes Berga3304b02012-03-28 11:04:24 +02001243 if (txq_params->ac >= NL80211_NUM_ACS)
1244 return -EINVAL;
1245
Jouni Malinen31888482008-10-30 16:59:24 +02001246 return 0;
1247}
1248
Johannes Bergf444de02010-05-05 15:25:02 +02001249static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1250{
1251 /*
Johannes Bergcc1d2802012-05-16 23:50:20 +02001252 * You can only set the channel explicitly for WDS interfaces,
1253 * all others have their channel managed via their respective
1254 * "establish a connection" command (connect, join, ...)
1255 *
1256 * For AP/GO and mesh mode, the channel can be set with the
1257 * channel userspace API, but is only stored and passed to the
1258 * low-level driver when the AP starts or the mesh is joined.
1259 * This is for backward compatibility, userspace can also give
1260 * the channel in the start-ap or join-mesh commands instead.
Johannes Bergf444de02010-05-05 15:25:02 +02001261 *
1262 * Monitors are special as they are normally slaved to
Johannes Berge8c9bd52012-06-06 08:18:22 +02001263 * whatever else is going on, so they have their own special
1264 * operation to set the monitor channel if possible.
Johannes Bergf444de02010-05-05 15:25:02 +02001265 */
1266 return !wdev ||
1267 wdev->iftype == NL80211_IFTYPE_AP ||
Johannes Bergf444de02010-05-05 15:25:02 +02001268 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001269 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1270 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001271}
1272
Johannes Bergcd6c6592012-05-10 21:27:18 +02001273static bool nl80211_valid_channel_type(struct genl_info *info,
1274 enum nl80211_channel_type *channel_type)
1275{
1276 enum nl80211_channel_type tmp;
1277
1278 if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
1279 return false;
1280
1281 tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1282 if (tmp != NL80211_CHAN_NO_HT &&
1283 tmp != NL80211_CHAN_HT20 &&
1284 tmp != NL80211_CHAN_HT40PLUS &&
1285 tmp != NL80211_CHAN_HT40MINUS)
1286 return false;
1287
1288 if (channel_type)
1289 *channel_type = tmp;
1290
1291 return true;
1292}
1293
Johannes Bergf444de02010-05-05 15:25:02 +02001294static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1295 struct wireless_dev *wdev,
1296 struct genl_info *info)
1297{
Johannes Bergaa430da2012-05-16 23:50:18 +02001298 struct ieee80211_channel *channel;
Johannes Bergf444de02010-05-05 15:25:02 +02001299 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1300 u32 freq;
1301 int result;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001302 enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
1303
1304 if (wdev)
1305 iftype = wdev->iftype;
Johannes Bergf444de02010-05-05 15:25:02 +02001306
1307 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1308 return -EINVAL;
1309
1310 if (!nl80211_can_set_dev_channel(wdev))
1311 return -EOPNOTSUPP;
1312
Johannes Bergcd6c6592012-05-10 21:27:18 +02001313 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
1314 !nl80211_valid_channel_type(info, &channel_type))
1315 return -EINVAL;
Johannes Bergf444de02010-05-05 15:25:02 +02001316
1317 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1318
1319 mutex_lock(&rdev->devlist_mtx);
Johannes Berge8c9bd52012-06-06 08:18:22 +02001320 switch (iftype) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001321 case NL80211_IFTYPE_AP:
1322 case NL80211_IFTYPE_P2P_GO:
1323 if (wdev->beacon_interval) {
1324 result = -EBUSY;
1325 break;
1326 }
1327 channel = rdev_freq_to_chan(rdev, freq, channel_type);
1328 if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
1329 channel,
1330 channel_type)) {
1331 result = -EINVAL;
1332 break;
1333 }
1334 wdev->preset_chan = channel;
1335 wdev->preset_chantype = channel_type;
1336 result = 0;
1337 break;
Johannes Bergcc1d2802012-05-16 23:50:20 +02001338 case NL80211_IFTYPE_MESH_POINT:
1339 result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
1340 break;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001341 case NL80211_IFTYPE_MONITOR:
1342 result = cfg80211_set_monitor_channel(rdev, freq, channel_type);
1343 break;
Johannes Bergaa430da2012-05-16 23:50:18 +02001344 default:
Johannes Berge8c9bd52012-06-06 08:18:22 +02001345 result = -EINVAL;
Johannes Bergf444de02010-05-05 15:25:02 +02001346 }
1347 mutex_unlock(&rdev->devlist_mtx);
1348
1349 return result;
1350}
1351
1352static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1353{
Johannes Berg4c476992010-10-04 21:36:35 +02001354 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1355 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001356
Johannes Berg4c476992010-10-04 21:36:35 +02001357 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001358}
1359
Bill Jordane8347eb2010-10-01 13:54:28 -04001360static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1361{
Johannes Berg43b19952010-10-07 13:10:30 +02001362 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1363 struct net_device *dev = info->user_ptr[1];
1364 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001365 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001366
1367 if (!info->attrs[NL80211_ATTR_MAC])
1368 return -EINVAL;
1369
Johannes Berg43b19952010-10-07 13:10:30 +02001370 if (netif_running(dev))
1371 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001372
Johannes Berg43b19952010-10-07 13:10:30 +02001373 if (!rdev->ops->set_wds_peer)
1374 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001375
Johannes Berg43b19952010-10-07 13:10:30 +02001376 if (wdev->iftype != NL80211_IFTYPE_WDS)
1377 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001378
1379 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001380 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001381}
1382
1383
Johannes Berg55682962007-09-20 13:09:35 -04001384static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1385{
1386 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001387 struct net_device *netdev = NULL;
1388 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001389 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001390 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001391 u32 changed;
1392 u8 retry_short = 0, retry_long = 0;
1393 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001394 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001395
Johannes Bergf444de02010-05-05 15:25:02 +02001396 /*
1397 * Try to find the wiphy and netdev. Normally this
1398 * function shouldn't need the netdev, but this is
1399 * done for backward compatibility -- previously
1400 * setting the channel was done per wiphy, but now
1401 * it is per netdev. Previous userland like hostapd
1402 * also passed a netdev to set_wiphy, so that it is
1403 * possible to let that go to the right netdev!
1404 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001405 mutex_lock(&cfg80211_mutex);
1406
Johannes Bergf444de02010-05-05 15:25:02 +02001407 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1408 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1409
1410 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1411 if (netdev && netdev->ieee80211_ptr) {
1412 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1413 mutex_lock(&rdev->mtx);
1414 } else
1415 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001416 }
1417
Johannes Bergf444de02010-05-05 15:25:02 +02001418 if (!netdev) {
1419 rdev = __cfg80211_rdev_from_info(info);
1420 if (IS_ERR(rdev)) {
1421 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001422 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001423 }
1424 wdev = NULL;
1425 netdev = NULL;
1426 result = 0;
1427
1428 mutex_lock(&rdev->mtx);
Johannes Bergcc1d2802012-05-16 23:50:20 +02001429 } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
Johannes Bergf444de02010-05-05 15:25:02 +02001430 wdev = netdev->ieee80211_ptr;
1431 else
1432 wdev = NULL;
1433
1434 /*
1435 * end workaround code, by now the rdev is available
1436 * and locked, and wdev may or may not be NULL.
1437 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001438
1439 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001440 result = cfg80211_dev_rename(
1441 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001442
1443 mutex_unlock(&cfg80211_mutex);
1444
1445 if (result)
1446 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001447
Jouni Malinen31888482008-10-30 16:59:24 +02001448 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1449 struct ieee80211_txq_params txq_params;
1450 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1451
1452 if (!rdev->ops->set_txq_params) {
1453 result = -EOPNOTSUPP;
1454 goto bad_res;
1455 }
1456
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001457 if (!netdev) {
1458 result = -EINVAL;
1459 goto bad_res;
1460 }
1461
Johannes Berg133a3ff2011-11-03 14:50:13 +01001462 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1463 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1464 result = -EINVAL;
1465 goto bad_res;
1466 }
1467
Johannes Berg2b5f8b02012-04-02 10:51:55 +02001468 if (!netif_running(netdev)) {
1469 result = -ENETDOWN;
1470 goto bad_res;
1471 }
1472
Jouni Malinen31888482008-10-30 16:59:24 +02001473 nla_for_each_nested(nl_txq_params,
1474 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1475 rem_txq_params) {
1476 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1477 nla_data(nl_txq_params),
1478 nla_len(nl_txq_params),
1479 txq_params_policy);
1480 result = parse_txq_params(tb, &txq_params);
1481 if (result)
1482 goto bad_res;
1483
1484 result = rdev->ops->set_txq_params(&rdev->wiphy,
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001485 netdev,
Jouni Malinen31888482008-10-30 16:59:24 +02001486 &txq_params);
1487 if (result)
1488 goto bad_res;
1489 }
1490 }
1491
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001492 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001493 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001494 if (result)
1495 goto bad_res;
1496 }
1497
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001498 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1499 enum nl80211_tx_power_setting type;
1500 int idx, mbm = 0;
1501
1502 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001503 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001504 goto bad_res;
1505 }
1506
1507 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1508 type = nla_get_u32(info->attrs[idx]);
1509
1510 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1511 (type != NL80211_TX_POWER_AUTOMATIC)) {
1512 result = -EINVAL;
1513 goto bad_res;
1514 }
1515
1516 if (type != NL80211_TX_POWER_AUTOMATIC) {
1517 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1518 mbm = nla_get_u32(info->attrs[idx]);
1519 }
1520
1521 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1522 if (result)
1523 goto bad_res;
1524 }
1525
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001526 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1527 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1528 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001529 if ((!rdev->wiphy.available_antennas_tx &&
1530 !rdev->wiphy.available_antennas_rx) ||
1531 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001532 result = -EOPNOTSUPP;
1533 goto bad_res;
1534 }
1535
1536 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1537 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1538
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001539 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001540 * available antenna masks, except for the "all" mask */
1541 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1542 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001543 result = -EINVAL;
1544 goto bad_res;
1545 }
1546
Bruno Randolf7f531e02010-12-16 11:30:22 +09001547 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1548 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001549
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001550 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1551 if (result)
1552 goto bad_res;
1553 }
1554
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001555 changed = 0;
1556
1557 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1558 retry_short = nla_get_u8(
1559 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1560 if (retry_short == 0) {
1561 result = -EINVAL;
1562 goto bad_res;
1563 }
1564 changed |= WIPHY_PARAM_RETRY_SHORT;
1565 }
1566
1567 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1568 retry_long = nla_get_u8(
1569 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1570 if (retry_long == 0) {
1571 result = -EINVAL;
1572 goto bad_res;
1573 }
1574 changed |= WIPHY_PARAM_RETRY_LONG;
1575 }
1576
1577 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1578 frag_threshold = nla_get_u32(
1579 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1580 if (frag_threshold < 256) {
1581 result = -EINVAL;
1582 goto bad_res;
1583 }
1584 if (frag_threshold != (u32) -1) {
1585 /*
1586 * Fragments (apart from the last one) are required to
1587 * have even length. Make the fragmentation code
1588 * simpler by stripping LSB should someone try to use
1589 * odd threshold value.
1590 */
1591 frag_threshold &= ~0x1;
1592 }
1593 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1594 }
1595
1596 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1597 rts_threshold = nla_get_u32(
1598 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1599 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1600 }
1601
Lukáš Turek81077e82009-12-21 22:50:47 +01001602 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1603 coverage_class = nla_get_u8(
1604 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1605 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1606 }
1607
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001608 if (changed) {
1609 u8 old_retry_short, old_retry_long;
1610 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001611 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001612
1613 if (!rdev->ops->set_wiphy_params) {
1614 result = -EOPNOTSUPP;
1615 goto bad_res;
1616 }
1617
1618 old_retry_short = rdev->wiphy.retry_short;
1619 old_retry_long = rdev->wiphy.retry_long;
1620 old_frag_threshold = rdev->wiphy.frag_threshold;
1621 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001622 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001623
1624 if (changed & WIPHY_PARAM_RETRY_SHORT)
1625 rdev->wiphy.retry_short = retry_short;
1626 if (changed & WIPHY_PARAM_RETRY_LONG)
1627 rdev->wiphy.retry_long = retry_long;
1628 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1629 rdev->wiphy.frag_threshold = frag_threshold;
1630 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1631 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001632 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1633 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001634
1635 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1636 if (result) {
1637 rdev->wiphy.retry_short = old_retry_short;
1638 rdev->wiphy.retry_long = old_retry_long;
1639 rdev->wiphy.frag_threshold = old_frag_threshold;
1640 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001641 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001642 }
1643 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001644
Johannes Berg306d6112008-12-08 12:39:04 +01001645 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001646 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001647 if (netdev)
1648 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001649 return result;
1650}
1651
1652
1653static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001654 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001655 struct net_device *dev)
1656{
1657 void *hdr;
1658
1659 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1660 if (!hdr)
1661 return -1;
1662
David S. Miller9360ffd2012-03-29 04:41:26 -04001663 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
1664 nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
1665 nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) ||
1666 nla_put_u32(msg, NL80211_ATTR_IFTYPE,
1667 dev->ieee80211_ptr->iftype) ||
1668 nla_put_u32(msg, NL80211_ATTR_GENERATION,
1669 rdev->devlist_generation ^
1670 (cfg80211_rdev_list_generation << 2)))
1671 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02001672
Pontus Fuchsd91df0e2012-04-03 16:39:58 +02001673 if (rdev->ops->get_channel) {
1674 struct ieee80211_channel *chan;
1675 enum nl80211_channel_type channel_type;
1676
1677 chan = rdev->ops->get_channel(&rdev->wiphy, &channel_type);
John W. Linville59ef43e2012-04-18 14:17:13 -04001678 if (chan &&
1679 (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
1680 chan->center_freq) ||
1681 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
1682 channel_type)))
1683 goto nla_put_failure;
Pontus Fuchsd91df0e2012-04-03 16:39:58 +02001684 }
1685
Johannes Berg55682962007-09-20 13:09:35 -04001686 return genlmsg_end(msg, hdr);
1687
1688 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001689 genlmsg_cancel(msg, hdr);
1690 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001691}
1692
1693static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1694{
1695 int wp_idx = 0;
1696 int if_idx = 0;
1697 int wp_start = cb->args[0];
1698 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001699 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001700 struct wireless_dev *wdev;
1701
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001702 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001703 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1704 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001705 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001706 if (wp_idx < wp_start) {
1707 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001708 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001709 }
Johannes Berg55682962007-09-20 13:09:35 -04001710 if_idx = 0;
1711
Johannes Bergf5ea9122009-08-07 16:17:38 +02001712 mutex_lock(&rdev->devlist_mtx);
1713 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001714 if (if_idx < if_start) {
1715 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001716 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001717 }
Johannes Berg55682962007-09-20 13:09:35 -04001718 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1719 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001720 rdev, wdev->netdev) < 0) {
1721 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001722 goto out;
1723 }
1724 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001725 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001726 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001727
1728 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001729 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001730 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001731 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001732
1733 cb->args[0] = wp_idx;
1734 cb->args[1] = if_idx;
1735
1736 return skb->len;
1737}
1738
1739static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1740{
1741 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001742 struct cfg80211_registered_device *dev = info->user_ptr[0];
1743 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001744
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001745 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001746 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001747 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001748
Johannes Bergd7264052009-04-19 16:23:20 +02001749 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001750 dev, netdev) < 0) {
1751 nlmsg_free(msg);
1752 return -ENOBUFS;
1753 }
Johannes Berg55682962007-09-20 13:09:35 -04001754
Johannes Berg134e6372009-07-10 09:51:34 +00001755 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001756}
1757
Michael Wu66f7ac52008-01-31 19:48:22 +01001758static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1759 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1760 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1761 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1762 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1763 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1764};
1765
1766static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1767{
1768 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1769 int flag;
1770
1771 *mntrflags = 0;
1772
1773 if (!nla)
1774 return -EINVAL;
1775
1776 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1777 nla, mntr_flags_policy))
1778 return -EINVAL;
1779
1780 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1781 if (flags[flag])
1782 *mntrflags |= (1<<flag);
1783
1784 return 0;
1785}
1786
Johannes Berg9bc383d2009-11-19 11:55:19 +01001787static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001788 struct net_device *netdev, u8 use_4addr,
1789 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001790{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001791 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001792 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001793 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001794 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001795 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001796
1797 switch (iftype) {
1798 case NL80211_IFTYPE_AP_VLAN:
1799 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1800 return 0;
1801 break;
1802 case NL80211_IFTYPE_STATION:
1803 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1804 return 0;
1805 break;
1806 default:
1807 break;
1808 }
1809
1810 return -EOPNOTSUPP;
1811}
1812
Johannes Berg55682962007-09-20 13:09:35 -04001813static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1814{
Johannes Berg4c476992010-10-04 21:36:35 +02001815 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001816 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001817 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001818 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001819 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001820 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001821 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001822
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001823 memset(&params, 0, sizeof(params));
1824
Johannes Berg04a773a2009-04-19 21:24:32 +02001825 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001826
Johannes Berg723b0382008-09-16 20:22:09 +02001827 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001828 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001829 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001830 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001831 if (ntype > NL80211_IFTYPE_MAX)
1832 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001833 }
1834
Johannes Berg92ffe052008-09-16 20:39:36 +02001835 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001836 struct wireless_dev *wdev = dev->ieee80211_ptr;
1837
Johannes Berg4c476992010-10-04 21:36:35 +02001838 if (ntype != NL80211_IFTYPE_MESH_POINT)
1839 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001840 if (netif_running(dev))
1841 return -EBUSY;
1842
1843 wdev_lock(wdev);
1844 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1845 IEEE80211_MAX_MESH_ID_LEN);
1846 wdev->mesh_id_up_len =
1847 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1848 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1849 wdev->mesh_id_up_len);
1850 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001851 }
1852
Felix Fietkau8b787642009-11-10 18:53:10 +01001853 if (info->attrs[NL80211_ATTR_4ADDR]) {
1854 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1855 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001856 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001857 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001858 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001859 } else {
1860 params.use_4addr = -1;
1861 }
1862
Johannes Berg92ffe052008-09-16 20:39:36 +02001863 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001864 if (ntype != NL80211_IFTYPE_MONITOR)
1865 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001866 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1867 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001868 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001869 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001870
1871 flags = &_flags;
1872 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001873 }
Johannes Berg3b858752009-03-12 09:55:09 +01001874
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001875 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001876 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001877 else
1878 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001879
Johannes Berg9bc383d2009-11-19 11:55:19 +01001880 if (!err && params.use_4addr != -1)
1881 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1882
Johannes Berg55682962007-09-20 13:09:35 -04001883 return err;
1884}
1885
1886static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1887{
Johannes Berg4c476992010-10-04 21:36:35 +02001888 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001889 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001890 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001891 int err;
1892 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001893 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001894
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001895 memset(&params, 0, sizeof(params));
1896
Johannes Berg55682962007-09-20 13:09:35 -04001897 if (!info->attrs[NL80211_ATTR_IFNAME])
1898 return -EINVAL;
1899
1900 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1901 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1902 if (type > NL80211_IFTYPE_MAX)
1903 return -EINVAL;
1904 }
1905
Johannes Berg79c97e92009-07-07 03:56:12 +02001906 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001907 !(rdev->wiphy.interface_modes & (1 << type)))
1908 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001909
Johannes Berg9bc383d2009-11-19 11:55:19 +01001910 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001911 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001912 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001913 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001914 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001915 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001916
Michael Wu66f7ac52008-01-31 19:48:22 +01001917 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1918 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1919 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001920 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001921 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001922 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001923 if (IS_ERR(dev))
1924 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001925
Johannes Berg29cbe682010-12-03 09:20:44 +01001926 if (type == NL80211_IFTYPE_MESH_POINT &&
1927 info->attrs[NL80211_ATTR_MESH_ID]) {
1928 struct wireless_dev *wdev = dev->ieee80211_ptr;
1929
1930 wdev_lock(wdev);
1931 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1932 IEEE80211_MAX_MESH_ID_LEN);
1933 wdev->mesh_id_up_len =
1934 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1935 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1936 wdev->mesh_id_up_len);
1937 wdev_unlock(wdev);
1938 }
1939
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001940 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001941}
1942
1943static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1944{
Johannes Berg4c476992010-10-04 21:36:35 +02001945 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1946 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001947
Johannes Berg4c476992010-10-04 21:36:35 +02001948 if (!rdev->ops->del_virtual_intf)
1949 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001950
Johannes Berg4c476992010-10-04 21:36:35 +02001951 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001952}
1953
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01001954static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
1955{
1956 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1957 struct net_device *dev = info->user_ptr[1];
1958 u16 noack_map;
1959
1960 if (!info->attrs[NL80211_ATTR_NOACK_MAP])
1961 return -EINVAL;
1962
1963 if (!rdev->ops->set_noack_map)
1964 return -EOPNOTSUPP;
1965
1966 noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
1967
1968 return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
1969}
1970
Johannes Berg41ade002007-12-19 02:03:29 +01001971struct get_key_cookie {
1972 struct sk_buff *msg;
1973 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001974 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001975};
1976
1977static void get_key_callback(void *c, struct key_params *params)
1978{
Johannes Bergb9454e82009-07-08 13:29:08 +02001979 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001980 struct get_key_cookie *cookie = c;
1981
David S. Miller9360ffd2012-03-29 04:41:26 -04001982 if ((params->key &&
1983 nla_put(cookie->msg, NL80211_ATTR_KEY_DATA,
1984 params->key_len, params->key)) ||
1985 (params->seq &&
1986 nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ,
1987 params->seq_len, params->seq)) ||
1988 (params->cipher &&
1989 nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1990 params->cipher)))
1991 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01001992
Johannes Bergb9454e82009-07-08 13:29:08 +02001993 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1994 if (!key)
1995 goto nla_put_failure;
1996
David S. Miller9360ffd2012-03-29 04:41:26 -04001997 if ((params->key &&
1998 nla_put(cookie->msg, NL80211_KEY_DATA,
1999 params->key_len, params->key)) ||
2000 (params->seq &&
2001 nla_put(cookie->msg, NL80211_KEY_SEQ,
2002 params->seq_len, params->seq)) ||
2003 (params->cipher &&
2004 nla_put_u32(cookie->msg, NL80211_KEY_CIPHER,
2005 params->cipher)))
2006 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02002007
David S. Miller9360ffd2012-03-29 04:41:26 -04002008 if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
2009 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02002010
2011 nla_nest_end(cookie->msg, key);
2012
Johannes Berg41ade002007-12-19 02:03:29 +01002013 return;
2014 nla_put_failure:
2015 cookie->error = 1;
2016}
2017
2018static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
2019{
Johannes Berg4c476992010-10-04 21:36:35 +02002020 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002021 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002022 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002023 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02002024 const u8 *mac_addr = NULL;
2025 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01002026 struct get_key_cookie cookie = {
2027 .error = 0,
2028 };
2029 void *hdr;
2030 struct sk_buff *msg;
2031
2032 if (info->attrs[NL80211_ATTR_KEY_IDX])
2033 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
2034
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02002035 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01002036 return -EINVAL;
2037
2038 if (info->attrs[NL80211_ATTR_MAC])
2039 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2040
Johannes Berge31b8212010-10-05 19:39:30 +02002041 pairwise = !!mac_addr;
2042 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
2043 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
2044 if (kt >= NUM_NL80211_KEYTYPES)
2045 return -EINVAL;
2046 if (kt != NL80211_KEYTYPE_GROUP &&
2047 kt != NL80211_KEYTYPE_PAIRWISE)
2048 return -EINVAL;
2049 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
2050 }
2051
Johannes Berg4c476992010-10-04 21:36:35 +02002052 if (!rdev->ops->get_key)
2053 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002054
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002055 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02002056 if (!msg)
2057 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01002058
2059 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
2060 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02002061 if (IS_ERR(hdr))
2062 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01002063
2064 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02002065 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01002066
David S. Miller9360ffd2012-03-29 04:41:26 -04002067 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
2068 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
2069 goto nla_put_failure;
2070 if (mac_addr &&
2071 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
2072 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01002073
Johannes Berge31b8212010-10-05 19:39:30 +02002074 if (pairwise && mac_addr &&
2075 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2076 return -ENOENT;
2077
2078 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
2079 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01002080
2081 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002082 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01002083
2084 if (cookie.error)
2085 goto nla_put_failure;
2086
2087 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02002088 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01002089
2090 nla_put_failure:
2091 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002092 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01002093 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01002094 return err;
2095}
2096
2097static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
2098{
Johannes Berg4c476992010-10-04 21:36:35 +02002099 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02002100 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002101 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002102 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002103
Johannes Bergb9454e82009-07-08 13:29:08 +02002104 err = nl80211_parse_key(info, &key);
2105 if (err)
2106 return err;
2107
2108 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01002109 return -EINVAL;
2110
Johannes Bergb9454e82009-07-08 13:29:08 +02002111 /* only support setting default key */
2112 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01002113 return -EINVAL;
2114
Johannes Bergfffd0932009-07-08 14:22:54 +02002115 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002116
2117 if (key.def) {
2118 if (!rdev->ops->set_default_key) {
2119 err = -EOPNOTSUPP;
2120 goto out;
2121 }
2122
2123 err = nl80211_key_allowed(dev->ieee80211_ptr);
2124 if (err)
2125 goto out;
2126
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002127 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
2128 key.def_uni, key.def_multi);
2129
2130 if (err)
2131 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02002132
Johannes Berg3d23e342009-09-29 23:27:28 +02002133#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002134 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02002135#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002136 } else {
2137 if (key.def_uni || !key.def_multi) {
2138 err = -EINVAL;
2139 goto out;
2140 }
2141
2142 if (!rdev->ops->set_default_mgmt_key) {
2143 err = -EOPNOTSUPP;
2144 goto out;
2145 }
2146
2147 err = nl80211_key_allowed(dev->ieee80211_ptr);
2148 if (err)
2149 goto out;
2150
2151 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
2152 dev, key.idx);
2153 if (err)
2154 goto out;
2155
2156#ifdef CONFIG_CFG80211_WEXT
2157 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
2158#endif
2159 }
2160
2161 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02002162 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002163
Johannes Berg41ade002007-12-19 02:03:29 +01002164 return err;
2165}
2166
2167static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
2168{
Johannes Berg4c476992010-10-04 21:36:35 +02002169 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02002170 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002171 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02002172 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02002173 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01002174
Johannes Bergb9454e82009-07-08 13:29:08 +02002175 err = nl80211_parse_key(info, &key);
2176 if (err)
2177 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002178
Johannes Bergb9454e82009-07-08 13:29:08 +02002179 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01002180 return -EINVAL;
2181
Johannes Berg41ade002007-12-19 02:03:29 +01002182 if (info->attrs[NL80211_ATTR_MAC])
2183 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2184
Johannes Berge31b8212010-10-05 19:39:30 +02002185 if (key.type == -1) {
2186 if (mac_addr)
2187 key.type = NL80211_KEYTYPE_PAIRWISE;
2188 else
2189 key.type = NL80211_KEYTYPE_GROUP;
2190 }
2191
2192 /* for now */
2193 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2194 key.type != NL80211_KEYTYPE_GROUP)
2195 return -EINVAL;
2196
Johannes Berg4c476992010-10-04 21:36:35 +02002197 if (!rdev->ops->add_key)
2198 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002199
Johannes Berge31b8212010-10-05 19:39:30 +02002200 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
2201 key.type == NL80211_KEYTYPE_PAIRWISE,
2202 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02002203 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02002204
2205 wdev_lock(dev->ieee80211_ptr);
2206 err = nl80211_key_allowed(dev->ieee80211_ptr);
2207 if (!err)
2208 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02002209 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02002210 mac_addr, &key.p);
2211 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002212
Johannes Berg41ade002007-12-19 02:03:29 +01002213 return err;
2214}
2215
2216static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
2217{
Johannes Berg4c476992010-10-04 21:36:35 +02002218 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002219 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002220 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002221 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002222 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002223
Johannes Bergb9454e82009-07-08 13:29:08 +02002224 err = nl80211_parse_key(info, &key);
2225 if (err)
2226 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002227
2228 if (info->attrs[NL80211_ATTR_MAC])
2229 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2230
Johannes Berge31b8212010-10-05 19:39:30 +02002231 if (key.type == -1) {
2232 if (mac_addr)
2233 key.type = NL80211_KEYTYPE_PAIRWISE;
2234 else
2235 key.type = NL80211_KEYTYPE_GROUP;
2236 }
2237
2238 /* for now */
2239 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2240 key.type != NL80211_KEYTYPE_GROUP)
2241 return -EINVAL;
2242
Johannes Berg4c476992010-10-04 21:36:35 +02002243 if (!rdev->ops->del_key)
2244 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002245
Johannes Bergfffd0932009-07-08 14:22:54 +02002246 wdev_lock(dev->ieee80211_ptr);
2247 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002248
2249 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2250 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2251 err = -ENOENT;
2252
Johannes Bergfffd0932009-07-08 14:22:54 +02002253 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02002254 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
2255 key.type == NL80211_KEYTYPE_PAIRWISE,
2256 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002257
Johannes Berg3d23e342009-09-29 23:27:28 +02002258#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002259 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002260 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002261 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002262 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002263 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2264 }
2265#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002266 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002267
Johannes Berg41ade002007-12-19 02:03:29 +01002268 return err;
2269}
2270
Johannes Berg88600202012-02-13 15:17:18 +01002271static int nl80211_parse_beacon(struct genl_info *info,
2272 struct cfg80211_beacon_data *bcn)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002273{
Johannes Berg88600202012-02-13 15:17:18 +01002274 bool haveinfo = false;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002275
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002276 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2277 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2278 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2279 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002280 return -EINVAL;
2281
Johannes Berg88600202012-02-13 15:17:18 +01002282 memset(bcn, 0, sizeof(*bcn));
Johannes Berged1b6cc2007-12-19 02:03:32 +01002283
Johannes Berged1b6cc2007-12-19 02:03:32 +01002284 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
Johannes Berg88600202012-02-13 15:17:18 +01002285 bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2286 bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2287 if (!bcn->head_len)
2288 return -EINVAL;
2289 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002290 }
2291
2292 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
Johannes Berg88600202012-02-13 15:17:18 +01002293 bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2294 bcn->tail_len =
Johannes Berged1b6cc2007-12-19 02:03:32 +01002295 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
Johannes Berg88600202012-02-13 15:17:18 +01002296 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002297 }
2298
Johannes Berg4c476992010-10-04 21:36:35 +02002299 if (!haveinfo)
2300 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002301
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002302 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg88600202012-02-13 15:17:18 +01002303 bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2304 bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002305 }
2306
2307 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002308 bcn->proberesp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002309 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002310 bcn->proberesp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002311 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2312 }
2313
2314 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002315 bcn->assocresp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002316 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002317 bcn->assocresp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002318 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2319 }
2320
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002321 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002322 bcn->probe_resp =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002323 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002324 bcn->probe_resp_len =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002325 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2326 }
2327
Johannes Berg88600202012-02-13 15:17:18 +01002328 return 0;
2329}
2330
2331static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
2332{
2333 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2334 struct net_device *dev = info->user_ptr[1];
2335 struct wireless_dev *wdev = dev->ieee80211_ptr;
2336 struct cfg80211_ap_settings params;
2337 int err;
2338
2339 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2340 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2341 return -EOPNOTSUPP;
2342
2343 if (!rdev->ops->start_ap)
2344 return -EOPNOTSUPP;
2345
2346 if (wdev->beacon_interval)
2347 return -EALREADY;
2348
2349 memset(&params, 0, sizeof(params));
2350
2351 /* these are required for START_AP */
2352 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2353 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
2354 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2355 return -EINVAL;
2356
2357 err = nl80211_parse_beacon(info, &params.beacon);
2358 if (err)
2359 return err;
2360
2361 params.beacon_interval =
2362 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2363 params.dtim_period =
2364 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2365
2366 err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
2367 if (err)
2368 return err;
2369
2370 /*
2371 * In theory, some of these attributes should be required here
2372 * but since they were not used when the command was originally
2373 * added, keep them optional for old user space programs to let
2374 * them continue to work with drivers that do not need the
2375 * additional information -- drivers must check!
2376 */
2377 if (info->attrs[NL80211_ATTR_SSID]) {
2378 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2379 params.ssid_len =
2380 nla_len(info->attrs[NL80211_ATTR_SSID]);
2381 if (params.ssid_len == 0 ||
2382 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2383 return -EINVAL;
2384 }
2385
2386 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2387 params.hidden_ssid = nla_get_u32(
2388 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2389 if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
2390 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
2391 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2392 return -EINVAL;
2393 }
2394
2395 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2396
2397 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2398 params.auth_type = nla_get_u32(
2399 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2400 if (!nl80211_valid_auth_type(params.auth_type))
2401 return -EINVAL;
2402 } else
2403 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2404
2405 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2406 NL80211_MAX_NR_CIPHER_SUITES);
2407 if (err)
2408 return err;
2409
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +05302410 if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
2411 if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
2412 return -EOPNOTSUPP;
2413 params.inactivity_timeout = nla_get_u16(
2414 info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
2415 }
2416
Johannes Bergaa430da2012-05-16 23:50:18 +02002417 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2418 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
2419
2420 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
2421 !nl80211_valid_channel_type(info, &channel_type))
2422 return -EINVAL;
2423
2424 params.channel = rdev_freq_to_chan(rdev,
2425 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
2426 channel_type);
2427 if (!params.channel)
2428 return -EINVAL;
2429 params.channel_type = channel_type;
2430 } else if (wdev->preset_chan) {
2431 params.channel = wdev->preset_chan;
2432 params.channel_type = wdev->preset_chantype;
2433 } else
2434 return -EINVAL;
2435
2436 if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
2437 params.channel_type))
2438 return -EINVAL;
2439
Johannes Berg88600202012-02-13 15:17:18 +01002440 err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
2441 if (!err)
2442 wdev->beacon_interval = params.beacon_interval;
Johannes Berg56d18932011-05-09 18:41:15 +02002443 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002444}
2445
Johannes Berg88600202012-02-13 15:17:18 +01002446static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
2447{
2448 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2449 struct net_device *dev = info->user_ptr[1];
2450 struct wireless_dev *wdev = dev->ieee80211_ptr;
2451 struct cfg80211_beacon_data params;
2452 int err;
2453
2454 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2455 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2456 return -EOPNOTSUPP;
2457
2458 if (!rdev->ops->change_beacon)
2459 return -EOPNOTSUPP;
2460
2461 if (!wdev->beacon_interval)
2462 return -EINVAL;
2463
2464 err = nl80211_parse_beacon(info, &params);
2465 if (err)
2466 return err;
2467
2468 return rdev->ops->change_beacon(&rdev->wiphy, dev, &params);
2469}
2470
2471static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002472{
Johannes Berg4c476992010-10-04 21:36:35 +02002473 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2474 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002475 struct wireless_dev *wdev = dev->ieee80211_ptr;
2476 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002477
Johannes Berg88600202012-02-13 15:17:18 +01002478 if (!rdev->ops->stop_ap)
Johannes Berg4c476992010-10-04 21:36:35 +02002479 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002480
Johannes Berg074ac8d2010-09-16 14:58:22 +02002481 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002482 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2483 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002484
Johannes Berg88600202012-02-13 15:17:18 +01002485 if (!wdev->beacon_interval)
2486 return -ENOENT;
2487
2488 err = rdev->ops->stop_ap(&rdev->wiphy, dev);
Johannes Berg56d18932011-05-09 18:41:15 +02002489 if (!err)
2490 wdev->beacon_interval = 0;
2491 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002492}
2493
Johannes Berg5727ef12007-12-19 02:03:34 +01002494static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2495 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2496 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2497 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002498 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002499 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Bergd83023d2011-12-14 09:29:15 +01002500 [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002501};
2502
Johannes Bergeccb8e82009-05-11 21:57:56 +03002503static int parse_station_flags(struct genl_info *info,
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002504 enum nl80211_iftype iftype,
Johannes Bergeccb8e82009-05-11 21:57:56 +03002505 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002506{
2507 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002508 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002509 int flag;
2510
Johannes Bergeccb8e82009-05-11 21:57:56 +03002511 /*
2512 * Try parsing the new attribute first so userspace
2513 * can specify both for older kernels.
2514 */
2515 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2516 if (nla) {
2517 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002518
Johannes Bergeccb8e82009-05-11 21:57:56 +03002519 sta_flags = nla_data(nla);
2520 params->sta_flags_mask = sta_flags->mask;
2521 params->sta_flags_set = sta_flags->set;
2522 if ((params->sta_flags_mask |
2523 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2524 return -EINVAL;
2525 return 0;
2526 }
2527
2528 /* if present, parse the old attribute */
2529
2530 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002531 if (!nla)
2532 return 0;
2533
2534 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2535 nla, sta_flags_policy))
2536 return -EINVAL;
2537
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002538 /*
2539 * Only allow certain flags for interface types so that
2540 * other attributes are silently ignored. Remember that
2541 * this is backward compatibility code with old userspace
2542 * and shouldn't be hit in other cases anyway.
2543 */
2544 switch (iftype) {
2545 case NL80211_IFTYPE_AP:
2546 case NL80211_IFTYPE_AP_VLAN:
2547 case NL80211_IFTYPE_P2P_GO:
2548 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2549 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2550 BIT(NL80211_STA_FLAG_WME) |
2551 BIT(NL80211_STA_FLAG_MFP);
2552 break;
2553 case NL80211_IFTYPE_P2P_CLIENT:
2554 case NL80211_IFTYPE_STATION:
2555 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2556 BIT(NL80211_STA_FLAG_TDLS_PEER);
2557 break;
2558 case NL80211_IFTYPE_MESH_POINT:
2559 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2560 BIT(NL80211_STA_FLAG_MFP) |
2561 BIT(NL80211_STA_FLAG_AUTHORIZED);
2562 default:
2563 return -EINVAL;
2564 }
Johannes Berg5727ef12007-12-19 02:03:34 +01002565
Johannes Berg3383b5a2012-05-10 20:14:43 +02002566 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) {
2567 if (flags[flag]) {
Johannes Bergeccb8e82009-05-11 21:57:56 +03002568 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002569
Johannes Berg3383b5a2012-05-10 20:14:43 +02002570 /* no longer support new API additions in old API */
2571 if (flag > NL80211_STA_FLAG_MAX_OLD_API)
2572 return -EINVAL;
2573 }
2574 }
2575
Johannes Berg5727ef12007-12-19 02:03:34 +01002576 return 0;
2577}
2578
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002579static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2580 int attr)
2581{
2582 struct nlattr *rate;
2583 u16 bitrate;
2584
2585 rate = nla_nest_start(msg, attr);
2586 if (!rate)
2587 goto nla_put_failure;
2588
2589 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2590 bitrate = cfg80211_calculate_bitrate(info);
David S. Miller9360ffd2012-03-29 04:41:26 -04002591 if ((bitrate > 0 &&
2592 nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate)) ||
2593 ((info->flags & RATE_INFO_FLAGS_MCS) &&
2594 nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
2595 ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
2596 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) ||
2597 ((info->flags & RATE_INFO_FLAGS_SHORT_GI) &&
2598 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)))
2599 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002600
2601 nla_nest_end(msg, rate);
2602 return true;
2603
2604nla_put_failure:
2605 return false;
2606}
2607
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002608static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
John W. Linville66266b32012-03-15 13:25:41 -04002609 int flags,
2610 struct cfg80211_registered_device *rdev,
2611 struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002612 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002613{
2614 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002615 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002616
2617 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2618 if (!hdr)
2619 return -1;
2620
David S. Miller9360ffd2012-03-29 04:41:26 -04002621 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
2622 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
2623 nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
2624 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02002625
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002626 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2627 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002628 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04002629 if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
2630 nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2631 sinfo->connected_time))
2632 goto nla_put_failure;
2633 if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
2634 nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2635 sinfo->inactive_time))
2636 goto nla_put_failure;
2637 if ((sinfo->filled & STATION_INFO_RX_BYTES) &&
2638 nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
2639 sinfo->rx_bytes))
2640 goto nla_put_failure;
2641 if ((sinfo->filled & STATION_INFO_TX_BYTES) &&
2642 nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
2643 sinfo->tx_bytes))
2644 goto nla_put_failure;
2645 if ((sinfo->filled & STATION_INFO_LLID) &&
2646 nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
2647 goto nla_put_failure;
2648 if ((sinfo->filled & STATION_INFO_PLID) &&
2649 nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
2650 goto nla_put_failure;
2651 if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
2652 nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
2653 sinfo->plink_state))
2654 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04002655 switch (rdev->wiphy.signal_type) {
2656 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04002657 if ((sinfo->filled & STATION_INFO_SIGNAL) &&
2658 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
2659 sinfo->signal))
2660 goto nla_put_failure;
2661 if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
2662 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2663 sinfo->signal_avg))
2664 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04002665 break;
2666 default:
2667 break;
2668 }
Henning Rogge420e7fa2008-12-11 22:04:19 +01002669 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002670 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2671 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002672 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002673 }
2674 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2675 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2676 NL80211_STA_INFO_RX_BITRATE))
2677 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002678 }
David S. Miller9360ffd2012-03-29 04:41:26 -04002679 if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
2680 nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
2681 sinfo->rx_packets))
2682 goto nla_put_failure;
2683 if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
2684 nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
2685 sinfo->tx_packets))
2686 goto nla_put_failure;
2687 if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
2688 nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
2689 sinfo->tx_retries))
2690 goto nla_put_failure;
2691 if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
2692 nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
2693 sinfo->tx_failed))
2694 goto nla_put_failure;
2695 if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
2696 nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
2697 sinfo->beacon_loss_count))
2698 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07002699 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2700 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2701 if (!bss_param)
2702 goto nla_put_failure;
2703
David S. Miller9360ffd2012-03-29 04:41:26 -04002704 if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) &&
2705 nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
2706 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
2707 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
2708 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
2709 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
2710 nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2711 sinfo->bss_param.dtim_period) ||
2712 nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2713 sinfo->bss_param.beacon_interval))
2714 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07002715
2716 nla_nest_end(msg, bss_param);
2717 }
David S. Miller9360ffd2012-03-29 04:41:26 -04002718 if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
2719 nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
2720 sizeof(struct nl80211_sta_flag_update),
2721 &sinfo->sta_flags))
2722 goto nla_put_failure;
John W. Linville7eab0f62012-04-12 14:25:14 -04002723 if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
2724 nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
2725 sinfo->t_offset))
2726 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002727 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002728
David S. Miller9360ffd2012-03-29 04:41:26 -04002729 if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
2730 nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2731 sinfo->assoc_req_ies))
2732 goto nla_put_failure;
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002733
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002734 return genlmsg_end(msg, hdr);
2735
2736 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002737 genlmsg_cancel(msg, hdr);
2738 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002739}
2740
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002741static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002742 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002743{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002744 struct station_info sinfo;
2745 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002746 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002747 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002748 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002749 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002750
Johannes Berg67748892010-10-04 21:14:06 +02002751 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2752 if (err)
2753 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002754
2755 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002756 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002757 goto out_err;
2758 }
2759
Johannes Bergbba95fe2008-07-29 13:22:51 +02002760 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002761 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002762 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2763 mac_addr, &sinfo);
2764 if (err == -ENOENT)
2765 break;
2766 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002767 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002768
2769 if (nl80211_send_station(skb,
2770 NETLINK_CB(cb->skb).pid,
2771 cb->nlh->nlmsg_seq, NLM_F_MULTI,
John W. Linville66266b32012-03-15 13:25:41 -04002772 dev, netdev, mac_addr,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002773 &sinfo) < 0)
2774 goto out;
2775
2776 sta_idx++;
2777 }
2778
2779
2780 out:
2781 cb->args[1] = sta_idx;
2782 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002783 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002784 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002785
2786 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002787}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002788
Johannes Berg5727ef12007-12-19 02:03:34 +01002789static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2790{
Johannes Berg4c476992010-10-04 21:36:35 +02002791 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2792 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002793 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002794 struct sk_buff *msg;
2795 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002796 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002797
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002798 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002799
2800 if (!info->attrs[NL80211_ATTR_MAC])
2801 return -EINVAL;
2802
2803 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2804
Johannes Berg4c476992010-10-04 21:36:35 +02002805 if (!rdev->ops->get_station)
2806 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002807
Johannes Berg79c97e92009-07-07 03:56:12 +02002808 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002809 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002810 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002811
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002812 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002813 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002814 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002815
2816 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
John W. Linville66266b32012-03-15 13:25:41 -04002817 rdev, dev, mac_addr, &sinfo) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02002818 nlmsg_free(msg);
2819 return -ENOBUFS;
2820 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002821
Johannes Berg4c476992010-10-04 21:36:35 +02002822 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002823}
2824
2825/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002826 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002827 */
Johannes Berg80b99892011-11-18 16:23:01 +01002828static struct net_device *get_vlan(struct genl_info *info,
2829 struct cfg80211_registered_device *rdev)
Johannes Berg5727ef12007-12-19 02:03:34 +01002830{
Johannes Berg463d0182009-07-14 00:33:35 +02002831 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg80b99892011-11-18 16:23:01 +01002832 struct net_device *v;
2833 int ret;
Johannes Berg5727ef12007-12-19 02:03:34 +01002834
Johannes Berg80b99892011-11-18 16:23:01 +01002835 if (!vlanattr)
2836 return NULL;
2837
2838 v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
2839 if (!v)
2840 return ERR_PTR(-ENODEV);
2841
2842 if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
2843 ret = -EINVAL;
2844 goto error;
Johannes Berg5727ef12007-12-19 02:03:34 +01002845 }
Johannes Berg80b99892011-11-18 16:23:01 +01002846
2847 if (!netif_running(v)) {
2848 ret = -ENETDOWN;
2849 goto error;
2850 }
2851
2852 return v;
2853 error:
2854 dev_put(v);
2855 return ERR_PTR(ret);
Johannes Berg5727ef12007-12-19 02:03:34 +01002856}
2857
2858static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2859{
Johannes Berg4c476992010-10-04 21:36:35 +02002860 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002861 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002862 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002863 struct station_parameters params;
2864 u8 *mac_addr = NULL;
2865
2866 memset(&params, 0, sizeof(params));
2867
2868 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002869 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002870
2871 if (info->attrs[NL80211_ATTR_STA_AID])
2872 return -EINVAL;
2873
2874 if (!info->attrs[NL80211_ATTR_MAC])
2875 return -EINVAL;
2876
2877 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2878
2879 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2880 params.supported_rates =
2881 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2882 params.supported_rates_len =
2883 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2884 }
2885
2886 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2887 params.listen_interval =
2888 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2889
Jouni Malinen36aedc902008-08-25 11:58:58 +03002890 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2891 params.ht_capa =
2892 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2893
Johannes Bergbdd90d52011-12-14 12:20:27 +01002894 if (!rdev->ops->change_station)
2895 return -EOPNOTSUPP;
2896
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002897 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002898 return -EINVAL;
2899
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002900 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2901 params.plink_action =
2902 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2903
Javier Cardona9c3990a2011-05-03 16:57:11 -07002904 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2905 params.plink_state =
2906 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2907
Johannes Berga97f4422009-06-18 17:23:43 +02002908 switch (dev->ieee80211_ptr->iftype) {
2909 case NL80211_IFTYPE_AP:
2910 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002911 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002912 /* disallow mesh-specific things */
2913 if (params.plink_action)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002914 return -EINVAL;
2915
2916 /* TDLS can't be set, ... */
2917 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
2918 return -EINVAL;
2919 /*
2920 * ... but don't bother the driver with it. This works around
2921 * a hostapd/wpa_supplicant issue -- it always includes the
2922 * TLDS_PEER flag in the mask even for AP mode.
2923 */
2924 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2925
2926 /* accept only the listed bits */
2927 if (params.sta_flags_mask &
2928 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2929 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2930 BIT(NL80211_STA_FLAG_WME) |
2931 BIT(NL80211_STA_FLAG_MFP)))
2932 return -EINVAL;
2933
2934 /* must be last in here for error handling */
2935 params.vlan = get_vlan(info, rdev);
2936 if (IS_ERR(params.vlan))
2937 return PTR_ERR(params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002938 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002939 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002940 case NL80211_IFTYPE_STATION:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002941 /*
2942 * Don't allow userspace to change the TDLS_PEER flag,
2943 * but silently ignore attempts to change it since we
2944 * don't have state here to verify that it doesn't try
2945 * to change the flag.
2946 */
2947 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Antonio Quartulli267335d2012-01-31 20:25:47 +01002948 /* fall through */
2949 case NL80211_IFTYPE_ADHOC:
2950 /* disallow things sta doesn't support */
2951 if (params.plink_action)
2952 return -EINVAL;
2953 if (params.ht_capa)
2954 return -EINVAL;
2955 if (params.listen_interval >= 0)
2956 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002957 /* reject any changes other than AUTHORIZED */
2958 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2959 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002960 break;
2961 case NL80211_IFTYPE_MESH_POINT:
2962 /* disallow things mesh doesn't support */
2963 if (params.vlan)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002964 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002965 if (params.ht_capa)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002966 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002967 if (params.listen_interval >= 0)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002968 return -EINVAL;
2969 /*
2970 * No special handling for TDLS here -- the userspace
2971 * mesh code doesn't have this bug.
2972 */
Javier Cardonab39c48f2011-04-07 15:08:30 -07002973 if (params.sta_flags_mask &
2974 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002975 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002976 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Bergbdd90d52011-12-14 12:20:27 +01002977 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002978 break;
2979 default:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002980 return -EOPNOTSUPP;
Johannes Berg034d6552009-05-27 10:35:29 +02002981 }
2982
Johannes Bergbdd90d52011-12-14 12:20:27 +01002983 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01002984
Johannes Berg79c97e92009-07-07 03:56:12 +02002985 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002986
Johannes Berg5727ef12007-12-19 02:03:34 +01002987 if (params.vlan)
2988 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002989
Johannes Berg5727ef12007-12-19 02:03:34 +01002990 return err;
2991}
2992
Eliad Pellerc75786c2011-08-23 14:37:46 +03002993static struct nla_policy
2994nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
2995 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
2996 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
2997};
2998
Johannes Berg5727ef12007-12-19 02:03:34 +01002999static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
3000{
Johannes Berg4c476992010-10-04 21:36:35 +02003001 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01003002 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003003 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003004 struct station_parameters params;
3005 u8 *mac_addr = NULL;
3006
3007 memset(&params, 0, sizeof(params));
3008
3009 if (!info->attrs[NL80211_ATTR_MAC])
3010 return -EINVAL;
3011
Johannes Berg5727ef12007-12-19 02:03:34 +01003012 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
3013 return -EINVAL;
3014
3015 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
3016 return -EINVAL;
3017
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02003018 if (!info->attrs[NL80211_ATTR_STA_AID])
3019 return -EINVAL;
3020
Johannes Berg5727ef12007-12-19 02:03:34 +01003021 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3022 params.supported_rates =
3023 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3024 params.supported_rates_len =
3025 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
3026 params.listen_interval =
3027 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02003028
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02003029 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
3030 if (!params.aid || params.aid > IEEE80211_MAX_AID)
3031 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02003032
Jouni Malinen36aedc902008-08-25 11:58:58 +03003033 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
3034 params.ht_capa =
3035 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01003036
Javier Cardona96b78df2011-04-07 15:08:33 -07003037 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
3038 params.plink_action =
3039 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
3040
Johannes Bergbdd90d52011-12-14 12:20:27 +01003041 if (!rdev->ops->add_station)
3042 return -EOPNOTSUPP;
3043
Johannes Bergbdd3ae32012-01-02 13:30:03 +01003044 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01003045 return -EINVAL;
3046
Johannes Bergbdd90d52011-12-14 12:20:27 +01003047 switch (dev->ieee80211_ptr->iftype) {
3048 case NL80211_IFTYPE_AP:
3049 case NL80211_IFTYPE_AP_VLAN:
3050 case NL80211_IFTYPE_P2P_GO:
3051 /* parse WME attributes if sta is WME capable */
3052 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
3053 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
3054 info->attrs[NL80211_ATTR_STA_WME]) {
3055 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
3056 struct nlattr *nla;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003057
Johannes Bergbdd90d52011-12-14 12:20:27 +01003058 nla = info->attrs[NL80211_ATTR_STA_WME];
3059 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
3060 nl80211_sta_wme_policy);
3061 if (err)
3062 return err;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003063
Johannes Bergbdd90d52011-12-14 12:20:27 +01003064 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
3065 params.uapsd_queues =
3066 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
3067 if (params.uapsd_queues &
3068 ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
3069 return -EINVAL;
3070
3071 if (tb[NL80211_STA_WME_MAX_SP])
3072 params.max_sp =
3073 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
3074
3075 if (params.max_sp &
3076 ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
3077 return -EINVAL;
3078
3079 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
3080 }
3081 /* TDLS peers cannot be added */
3082 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003083 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003084 /* but don't bother the driver with it */
3085 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Eliad Pellerc75786c2011-08-23 14:37:46 +03003086
Johannes Bergbdd90d52011-12-14 12:20:27 +01003087 /* must be last in here for error handling */
3088 params.vlan = get_vlan(info, rdev);
3089 if (IS_ERR(params.vlan))
3090 return PTR_ERR(params.vlan);
3091 break;
3092 case NL80211_IFTYPE_MESH_POINT:
3093 /* TDLS peers cannot be added */
3094 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003095 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003096 break;
3097 case NL80211_IFTYPE_STATION:
3098 /* Only TDLS peers can be added */
3099 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
3100 return -EINVAL;
3101 /* Can only add if TDLS ... */
3102 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
3103 return -EOPNOTSUPP;
3104 /* ... with external setup is supported */
3105 if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
3106 return -EOPNOTSUPP;
3107 break;
3108 default:
3109 return -EOPNOTSUPP;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003110 }
3111
Johannes Bergbdd90d52011-12-14 12:20:27 +01003112 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01003113
Johannes Berg79c97e92009-07-07 03:56:12 +02003114 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01003115
Johannes Berg5727ef12007-12-19 02:03:34 +01003116 if (params.vlan)
3117 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01003118 return err;
3119}
3120
3121static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
3122{
Johannes Berg4c476992010-10-04 21:36:35 +02003123 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3124 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003125 u8 *mac_addr = NULL;
3126
3127 if (info->attrs[NL80211_ATTR_MAC])
3128 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3129
Johannes Berge80cf852009-05-11 14:43:13 +02003130 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02003131 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02003132 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02003133 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3134 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02003135
Johannes Berg4c476992010-10-04 21:36:35 +02003136 if (!rdev->ops->del_station)
3137 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01003138
Johannes Berg4c476992010-10-04 21:36:35 +02003139 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01003140}
3141
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003142static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
3143 int flags, struct net_device *dev,
3144 u8 *dst, u8 *next_hop,
3145 struct mpath_info *pinfo)
3146{
3147 void *hdr;
3148 struct nlattr *pinfoattr;
3149
3150 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
3151 if (!hdr)
3152 return -1;
3153
David S. Miller9360ffd2012-03-29 04:41:26 -04003154 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3155 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
3156 nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop) ||
3157 nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation))
3158 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02003159
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003160 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
3161 if (!pinfoattr)
3162 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003163 if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) &&
3164 nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
3165 pinfo->frame_qlen))
3166 goto nla_put_failure;
3167 if (((pinfo->filled & MPATH_INFO_SN) &&
3168 nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) ||
3169 ((pinfo->filled & MPATH_INFO_METRIC) &&
3170 nla_put_u32(msg, NL80211_MPATH_INFO_METRIC,
3171 pinfo->metric)) ||
3172 ((pinfo->filled & MPATH_INFO_EXPTIME) &&
3173 nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME,
3174 pinfo->exptime)) ||
3175 ((pinfo->filled & MPATH_INFO_FLAGS) &&
3176 nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS,
3177 pinfo->flags)) ||
3178 ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) &&
3179 nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
3180 pinfo->discovery_timeout)) ||
3181 ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
3182 nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
3183 pinfo->discovery_retries)))
3184 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003185
3186 nla_nest_end(msg, pinfoattr);
3187
3188 return genlmsg_end(msg, hdr);
3189
3190 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07003191 genlmsg_cancel(msg, hdr);
3192 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003193}
3194
3195static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003196 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003197{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003198 struct mpath_info pinfo;
3199 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003200 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003201 u8 dst[ETH_ALEN];
3202 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02003203 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003204 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003205
Johannes Berg67748892010-10-04 21:14:06 +02003206 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3207 if (err)
3208 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003209
3210 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003211 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003212 goto out_err;
3213 }
3214
Jouni Malineneec60b02009-03-20 21:21:19 +02003215 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
3216 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02003217 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02003218 }
3219
Johannes Bergbba95fe2008-07-29 13:22:51 +02003220 while (1) {
3221 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
3222 dst, next_hop, &pinfo);
3223 if (err == -ENOENT)
3224 break;
3225 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003226 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003227
3228 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
3229 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3230 netdev, dst, next_hop,
3231 &pinfo) < 0)
3232 goto out;
3233
3234 path_idx++;
3235 }
3236
3237
3238 out:
3239 cb->args[1] = path_idx;
3240 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003241 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003242 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003243 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003244}
3245
3246static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
3247{
Johannes Berg4c476992010-10-04 21:36:35 +02003248 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003249 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003250 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003251 struct mpath_info pinfo;
3252 struct sk_buff *msg;
3253 u8 *dst = NULL;
3254 u8 next_hop[ETH_ALEN];
3255
3256 memset(&pinfo, 0, sizeof(pinfo));
3257
3258 if (!info->attrs[NL80211_ATTR_MAC])
3259 return -EINVAL;
3260
3261 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3262
Johannes Berg4c476992010-10-04 21:36:35 +02003263 if (!rdev->ops->get_mpath)
3264 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003265
Johannes Berg4c476992010-10-04 21:36:35 +02003266 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3267 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003268
Johannes Berg79c97e92009-07-07 03:56:12 +02003269 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003270 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003271 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003272
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003273 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003274 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003275 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003276
3277 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02003278 dev, dst, next_hop, &pinfo) < 0) {
3279 nlmsg_free(msg);
3280 return -ENOBUFS;
3281 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003282
Johannes Berg4c476992010-10-04 21:36:35 +02003283 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003284}
3285
3286static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
3287{
Johannes Berg4c476992010-10-04 21:36:35 +02003288 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3289 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003290 u8 *dst = NULL;
3291 u8 *next_hop = NULL;
3292
3293 if (!info->attrs[NL80211_ATTR_MAC])
3294 return -EINVAL;
3295
3296 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3297 return -EINVAL;
3298
3299 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3300 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3301
Johannes Berg4c476992010-10-04 21:36:35 +02003302 if (!rdev->ops->change_mpath)
3303 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003304
Johannes Berg4c476992010-10-04 21:36:35 +02003305 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3306 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003307
Johannes Berg4c476992010-10-04 21:36:35 +02003308 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003309}
Johannes Berg4c476992010-10-04 21:36:35 +02003310
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003311static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
3312{
Johannes Berg4c476992010-10-04 21:36:35 +02003313 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3314 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003315 u8 *dst = NULL;
3316 u8 *next_hop = NULL;
3317
3318 if (!info->attrs[NL80211_ATTR_MAC])
3319 return -EINVAL;
3320
3321 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3322 return -EINVAL;
3323
3324 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3325 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3326
Johannes Berg4c476992010-10-04 21:36:35 +02003327 if (!rdev->ops->add_mpath)
3328 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003329
Johannes Berg4c476992010-10-04 21:36:35 +02003330 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3331 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003332
Johannes Berg4c476992010-10-04 21:36:35 +02003333 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003334}
3335
3336static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
3337{
Johannes Berg4c476992010-10-04 21:36:35 +02003338 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3339 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003340 u8 *dst = NULL;
3341
3342 if (info->attrs[NL80211_ATTR_MAC])
3343 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3344
Johannes Berg4c476992010-10-04 21:36:35 +02003345 if (!rdev->ops->del_mpath)
3346 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003347
Johannes Berg4c476992010-10-04 21:36:35 +02003348 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003349}
3350
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003351static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
3352{
Johannes Berg4c476992010-10-04 21:36:35 +02003353 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3354 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003355 struct bss_parameters params;
3356
3357 memset(&params, 0, sizeof(params));
3358 /* default to not changing parameters */
3359 params.use_cts_prot = -1;
3360 params.use_short_preamble = -1;
3361 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003362 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01003363 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003364
3365 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
3366 params.use_cts_prot =
3367 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
3368 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
3369 params.use_short_preamble =
3370 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
3371 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
3372 params.use_short_slot_time =
3373 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02003374 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
3375 params.basic_rates =
3376 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3377 params.basic_rates_len =
3378 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3379 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003380 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
3381 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01003382 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
3383 params.ht_opmode =
3384 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003385
Johannes Berg4c476992010-10-04 21:36:35 +02003386 if (!rdev->ops->change_bss)
3387 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003388
Johannes Berg074ac8d2010-09-16 14:58:22 +02003389 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02003390 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3391 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003392
Johannes Berg4c476992010-10-04 21:36:35 +02003393 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003394}
3395
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003396static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003397 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
3398 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
3399 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
3400 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
3401 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
3402 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
3403};
3404
3405static int parse_reg_rule(struct nlattr *tb[],
3406 struct ieee80211_reg_rule *reg_rule)
3407{
3408 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
3409 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
3410
3411 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
3412 return -EINVAL;
3413 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
3414 return -EINVAL;
3415 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
3416 return -EINVAL;
3417 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
3418 return -EINVAL;
3419 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
3420 return -EINVAL;
3421
3422 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
3423
3424 freq_range->start_freq_khz =
3425 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
3426 freq_range->end_freq_khz =
3427 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
3428 freq_range->max_bandwidth_khz =
3429 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
3430
3431 power_rule->max_eirp =
3432 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
3433
3434 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
3435 power_rule->max_antenna_gain =
3436 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
3437
3438 return 0;
3439}
3440
3441static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3442{
3443 int r;
3444 char *data = NULL;
3445
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003446 /*
3447 * You should only get this when cfg80211 hasn't yet initialized
3448 * completely when built-in to the kernel right between the time
3449 * window between nl80211_init() and regulatory_init(), if that is
3450 * even possible.
3451 */
3452 mutex_lock(&cfg80211_mutex);
3453 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003454 mutex_unlock(&cfg80211_mutex);
3455 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003456 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003457 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003458
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003459 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3460 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003461
3462 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3463
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003464 r = regulatory_hint_user(data);
3465
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003466 return r;
3467}
3468
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003469static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003470 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003471{
Johannes Berg4c476992010-10-04 21:36:35 +02003472 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003473 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003474 struct wireless_dev *wdev = dev->ieee80211_ptr;
3475 struct mesh_config cur_params;
3476 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003477 void *hdr;
3478 struct nlattr *pinfoattr;
3479 struct sk_buff *msg;
3480
Johannes Berg29cbe682010-12-03 09:20:44 +01003481 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3482 return -EOPNOTSUPP;
3483
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003484 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02003485 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02003486
Johannes Berg29cbe682010-12-03 09:20:44 +01003487 wdev_lock(wdev);
3488 /* If not connected, get default parameters */
3489 if (!wdev->mesh_id_len)
3490 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
3491 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003492 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003493 &cur_params);
3494 wdev_unlock(wdev);
3495
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003496 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003497 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003498
3499 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003500 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003501 if (!msg)
3502 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003503 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003504 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003505 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003506 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003507 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003508 if (!pinfoattr)
3509 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003510 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3511 nla_put_u16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
3512 cur_params.dot11MeshRetryTimeout) ||
3513 nla_put_u16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3514 cur_params.dot11MeshConfirmTimeout) ||
3515 nla_put_u16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
3516 cur_params.dot11MeshHoldingTimeout) ||
3517 nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
3518 cur_params.dot11MeshMaxPeerLinks) ||
3519 nla_put_u8(msg, NL80211_MESHCONF_MAX_RETRIES,
3520 cur_params.dot11MeshMaxRetries) ||
3521 nla_put_u8(msg, NL80211_MESHCONF_TTL,
3522 cur_params.dot11MeshTTL) ||
3523 nla_put_u8(msg, NL80211_MESHCONF_ELEMENT_TTL,
3524 cur_params.element_ttl) ||
3525 nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3526 cur_params.auto_open_plinks) ||
John W. Linville7eab0f62012-04-12 14:25:14 -04003527 nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
3528 cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04003529 nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3530 cur_params.dot11MeshHWMPmaxPREQretries) ||
3531 nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3532 cur_params.path_refresh_time) ||
3533 nla_put_u16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3534 cur_params.min_discovery_timeout) ||
3535 nla_put_u32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3536 cur_params.dot11MeshHWMPactivePathTimeout) ||
3537 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3538 cur_params.dot11MeshHWMPpreqMinInterval) ||
3539 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3540 cur_params.dot11MeshHWMPperrMinInterval) ||
3541 nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3542 cur_params.dot11MeshHWMPnetDiameterTraversalTime) ||
3543 nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3544 cur_params.dot11MeshHWMPRootMode) ||
3545 nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3546 cur_params.dot11MeshHWMPRannInterval) ||
3547 nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3548 cur_params.dot11MeshGateAnnouncementProtocol) ||
3549 nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
3550 cur_params.dot11MeshForwarding) ||
3551 nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
Ashok Nagarajan70c33ea2012-04-30 14:20:32 -07003552 cur_params.rssi_threshold) ||
3553 nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08003554 cur_params.ht_opmode) ||
3555 nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
3556 cur_params.dot11MeshHWMPactivePathToRootTimeout) ||
3557 nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08003558 cur_params.dot11MeshHWMProotInterval) ||
3559 nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
3560 cur_params.dot11MeshHWMPconfirmationInterval))
David S. Miller9360ffd2012-03-29 04:41:26 -04003561 goto nla_put_failure;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003562 nla_nest_end(msg, pinfoattr);
3563 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003564 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003565
Johannes Berg3b858752009-03-12 09:55:09 +01003566 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003567 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003568 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003569 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003570 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003571}
3572
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003573static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003574 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3575 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3576 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3577 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3578 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3579 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003580 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003581 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
Javier Cardonad299a1f2012-03-31 11:31:33 -07003582 [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003583 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3584 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3585 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3586 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3587 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
Thomas Pedersendca7e942011-11-24 17:15:24 -08003588 [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003589 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07003590 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07003591 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07003592 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003593 [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003594 [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 },
3595 [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 },
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08003596 [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
3597 [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08003598 [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003599};
3600
Javier Cardonac80d5452010-12-16 17:37:49 -08003601static const struct nla_policy
3602 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
Javier Cardonad299a1f2012-03-31 11:31:33 -07003603 [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
Javier Cardonac80d5452010-12-16 17:37:49 -08003604 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3605 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003606 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003607 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003608 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003609 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003610};
3611
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003612static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003613 struct mesh_config *cfg,
3614 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003615{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003616 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003617 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003618
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003619#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3620do {\
3621 if (table[attr_num]) {\
3622 cfg->param = nla_fn(table[attr_num]); \
3623 mask |= (1 << (attr_num - 1)); \
3624 } \
3625} while (0);\
3626
3627
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003628 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003629 return -EINVAL;
3630 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003631 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003632 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003633 return -EINVAL;
3634
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003635 /* This makes sure that there aren't more than 32 mesh config
3636 * parameters (otherwise our bitfield scheme would not work.) */
3637 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3638
3639 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003640 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003641 mask, NL80211_MESHCONF_RETRY_TIMEOUT,
3642 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003643 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003644 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3645 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003646 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003647 mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
3648 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003649 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003650 mask, NL80211_MESHCONF_MAX_PEER_LINKS,
3651 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003652 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003653 mask, NL80211_MESHCONF_MAX_RETRIES,
3654 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003655 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003656 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003657 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003658 mask, NL80211_MESHCONF_ELEMENT_TTL,
3659 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003660 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003661 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3662 nla_get_u8);
3663 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask,
3664 NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
3665 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003666 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003667 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3668 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003669 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003670 mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
3671 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003672 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003673 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3674 nla_get_u16);
3675 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask,
3676 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3677 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003678 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003679 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3680 nla_get_u16);
Thomas Pedersendca7e942011-11-24 17:15:24 -08003681 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003682 mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3683 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003684 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003685 dot11MeshHWMPnetDiameterTraversalTime, mask,
3686 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3687 nla_get_u16);
3688 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask,
3689 NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8);
3690 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask,
3691 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3692 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003693 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003694 dot11MeshGateAnnouncementProtocol, mask,
3695 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3696 nla_get_u8);
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003697 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003698 mask, NL80211_MESHCONF_FORWARDING,
3699 nla_get_u8);
Ashok Nagarajan55335132012-02-28 17:04:08 -08003700 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003701 mask, NL80211_MESHCONF_RSSI_THRESHOLD,
3702 nla_get_u32);
Ashok Nagarajan70c33ea2012-04-30 14:20:32 -07003703 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003704 mask, NL80211_MESHCONF_HT_OPMODE,
3705 nla_get_u16);
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08003706 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
3707 mask,
3708 NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
3709 nla_get_u32);
3710 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval,
3711 mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
3712 nla_get_u16);
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08003713 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3714 dot11MeshHWMPconfirmationInterval, mask,
3715 NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
3716 nla_get_u16);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003717 if (mask_out)
3718 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003719
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003720 return 0;
3721
3722#undef FILL_IN_MESH_PARAM_IF_SET
3723}
3724
Javier Cardonac80d5452010-12-16 17:37:49 -08003725static int nl80211_parse_mesh_setup(struct genl_info *info,
3726 struct mesh_setup *setup)
3727{
3728 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3729
3730 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3731 return -EINVAL;
3732 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3733 info->attrs[NL80211_ATTR_MESH_SETUP],
3734 nl80211_mesh_setup_params_policy))
3735 return -EINVAL;
3736
Javier Cardonad299a1f2012-03-31 11:31:33 -07003737 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
3738 setup->sync_method =
3739 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
3740 IEEE80211_SYNC_METHOD_VENDOR :
3741 IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
3742
Javier Cardonac80d5452010-12-16 17:37:49 -08003743 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3744 setup->path_sel_proto =
3745 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3746 IEEE80211_PATH_PROTOCOL_VENDOR :
3747 IEEE80211_PATH_PROTOCOL_HWMP;
3748
3749 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3750 setup->path_metric =
3751 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3752 IEEE80211_PATH_METRIC_VENDOR :
3753 IEEE80211_PATH_METRIC_AIRTIME;
3754
Javier Cardona581a8b02011-04-07 15:08:27 -07003755
3756 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003757 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003758 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003759 if (!is_valid_ie_attr(ieattr))
3760 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003761 setup->ie = nla_data(ieattr);
3762 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003763 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003764 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3765 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003766
3767 return 0;
3768}
3769
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003770static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003771 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003772{
3773 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3774 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003775 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003776 struct mesh_config cfg;
3777 u32 mask;
3778 int err;
3779
Johannes Berg29cbe682010-12-03 09:20:44 +01003780 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3781 return -EOPNOTSUPP;
3782
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003783 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003784 return -EOPNOTSUPP;
3785
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003786 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003787 if (err)
3788 return err;
3789
Johannes Berg29cbe682010-12-03 09:20:44 +01003790 wdev_lock(wdev);
3791 if (!wdev->mesh_id_len)
3792 err = -ENOLINK;
3793
3794 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003795 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003796 mask, &cfg);
3797
3798 wdev_unlock(wdev);
3799
3800 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003801}
3802
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003803static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3804{
3805 struct sk_buff *msg;
3806 void *hdr = NULL;
3807 struct nlattr *nl_reg_rules;
3808 unsigned int i;
3809 int err = -EINVAL;
3810
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003811 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003812
3813 if (!cfg80211_regdomain)
3814 goto out;
3815
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003816 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003817 if (!msg) {
3818 err = -ENOBUFS;
3819 goto out;
3820 }
3821
3822 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3823 NL80211_CMD_GET_REG);
3824 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003825 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003826
David S. Miller9360ffd2012-03-29 04:41:26 -04003827 if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
3828 cfg80211_regdomain->alpha2) ||
3829 (cfg80211_regdomain->dfs_region &&
3830 nla_put_u8(msg, NL80211_ATTR_DFS_REGION,
3831 cfg80211_regdomain->dfs_region)))
3832 goto nla_put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003833
3834 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3835 if (!nl_reg_rules)
3836 goto nla_put_failure;
3837
3838 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3839 struct nlattr *nl_reg_rule;
3840 const struct ieee80211_reg_rule *reg_rule;
3841 const struct ieee80211_freq_range *freq_range;
3842 const struct ieee80211_power_rule *power_rule;
3843
3844 reg_rule = &cfg80211_regdomain->reg_rules[i];
3845 freq_range = &reg_rule->freq_range;
3846 power_rule = &reg_rule->power_rule;
3847
3848 nl_reg_rule = nla_nest_start(msg, i);
3849 if (!nl_reg_rule)
3850 goto nla_put_failure;
3851
David S. Miller9360ffd2012-03-29 04:41:26 -04003852 if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3853 reg_rule->flags) ||
3854 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
3855 freq_range->start_freq_khz) ||
3856 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
3857 freq_range->end_freq_khz) ||
3858 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3859 freq_range->max_bandwidth_khz) ||
3860 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3861 power_rule->max_antenna_gain) ||
3862 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3863 power_rule->max_eirp))
3864 goto nla_put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003865
3866 nla_nest_end(msg, nl_reg_rule);
3867 }
3868
3869 nla_nest_end(msg, nl_reg_rules);
3870
3871 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003872 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003873 goto out;
3874
3875nla_put_failure:
3876 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003877put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003878 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003879 err = -EMSGSIZE;
3880out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003881 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003882 return err;
3883}
3884
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003885static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3886{
3887 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3888 struct nlattr *nl_reg_rule;
3889 char *alpha2 = NULL;
3890 int rem_reg_rules = 0, r = 0;
3891 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003892 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003893 struct ieee80211_regdomain *rd = NULL;
3894
3895 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3896 return -EINVAL;
3897
3898 if (!info->attrs[NL80211_ATTR_REG_RULES])
3899 return -EINVAL;
3900
3901 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3902
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003903 if (info->attrs[NL80211_ATTR_DFS_REGION])
3904 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
3905
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003906 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3907 rem_reg_rules) {
3908 num_rules++;
3909 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003910 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003911 }
3912
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003913 mutex_lock(&cfg80211_mutex);
3914
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003915 if (!reg_is_valid_request(alpha2)) {
3916 r = -EINVAL;
3917 goto bad_reg;
3918 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003919
3920 size_of_regd = sizeof(struct ieee80211_regdomain) +
3921 (num_rules * sizeof(struct ieee80211_reg_rule));
3922
3923 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003924 if (!rd) {
3925 r = -ENOMEM;
3926 goto bad_reg;
3927 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003928
3929 rd->n_reg_rules = num_rules;
3930 rd->alpha2[0] = alpha2[0];
3931 rd->alpha2[1] = alpha2[1];
3932
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003933 /*
3934 * Disable DFS master mode if the DFS region was
3935 * not supported or known on this kernel.
3936 */
3937 if (reg_supported_dfs_region(dfs_region))
3938 rd->dfs_region = dfs_region;
3939
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003940 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3941 rem_reg_rules) {
3942 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3943 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3944 reg_rule_policy);
3945 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3946 if (r)
3947 goto bad_reg;
3948
3949 rule_idx++;
3950
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003951 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3952 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003953 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003954 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003955 }
3956
3957 BUG_ON(rule_idx != num_rules);
3958
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003959 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003960
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003961 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003962
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003963 return r;
3964
Johannes Bergd2372b32008-10-24 20:32:20 +02003965 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003966 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003967 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003968 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003969}
3970
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003971static int validate_scan_freqs(struct nlattr *freqs)
3972{
3973 struct nlattr *attr1, *attr2;
3974 int n_channels = 0, tmp1, tmp2;
3975
3976 nla_for_each_nested(attr1, freqs, tmp1) {
3977 n_channels++;
3978 /*
3979 * Some hardware has a limited channel list for
3980 * scanning, and it is pretty much nonsensical
3981 * to scan for a channel twice, so disallow that
3982 * and don't require drivers to check that the
3983 * channel list they get isn't longer than what
3984 * they can scan, as long as they can scan all
3985 * the channels they registered at once.
3986 */
3987 nla_for_each_nested(attr2, freqs, tmp2)
3988 if (attr1 != attr2 &&
3989 nla_get_u32(attr1) == nla_get_u32(attr2))
3990 return 0;
3991 }
3992
3993 return n_channels;
3994}
3995
Johannes Berg2a519312009-02-10 21:25:55 +01003996static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3997{
Johannes Berg4c476992010-10-04 21:36:35 +02003998 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3999 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01004000 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01004001 struct nlattr *attr;
4002 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004003 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02004004 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01004005
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004006 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4007 return -EINVAL;
4008
Johannes Berg79c97e92009-07-07 03:56:12 +02004009 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01004010
Johannes Berg4c476992010-10-04 21:36:35 +02004011 if (!rdev->ops->scan)
4012 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01004013
Johannes Berg4c476992010-10-04 21:36:35 +02004014 if (rdev->scan_req)
4015 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01004016
4017 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004018 n_channels = validate_scan_freqs(
4019 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02004020 if (!n_channels)
4021 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01004022 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02004023 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02004024 n_channels = 0;
4025
Johannes Berg2a519312009-02-10 21:25:55 +01004026 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
4027 if (wiphy->bands[band])
4028 n_channels += wiphy->bands[band]->n_channels;
4029 }
4030
4031 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
4032 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
4033 n_ssids++;
4034
Johannes Berg4c476992010-10-04 21:36:35 +02004035 if (n_ssids > wiphy->max_scan_ssids)
4036 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01004037
Jouni Malinen70692ad2009-02-16 19:39:13 +02004038 if (info->attrs[NL80211_ATTR_IE])
4039 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4040 else
4041 ie_len = 0;
4042
Johannes Berg4c476992010-10-04 21:36:35 +02004043 if (ie_len > wiphy->max_scan_ie_len)
4044 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02004045
Johannes Berg2a519312009-02-10 21:25:55 +01004046 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004047 + sizeof(*request->ssids) * n_ssids
4048 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02004049 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02004050 if (!request)
4051 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01004052
Johannes Berg2a519312009-02-10 21:25:55 +01004053 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02004054 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01004055 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02004056 if (ie_len) {
4057 if (request->ssids)
4058 request->ie = (void *)(request->ssids + n_ssids);
4059 else
4060 request->ie = (void *)(request->channels + n_channels);
4061 }
Johannes Berg2a519312009-02-10 21:25:55 +01004062
Johannes Berg584991d2009-11-02 13:32:03 +01004063 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01004064 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4065 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01004066 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01004067 struct ieee80211_channel *chan;
4068
4069 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
4070
4071 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01004072 err = -EINVAL;
4073 goto out_free;
4074 }
Johannes Berg584991d2009-11-02 13:32:03 +01004075
4076 /* ignore disabled channels */
4077 if (chan->flags & IEEE80211_CHAN_DISABLED)
4078 continue;
4079
4080 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01004081 i++;
4082 }
4083 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02004084 enum ieee80211_band band;
4085
Johannes Berg2a519312009-02-10 21:25:55 +01004086 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01004087 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4088 int j;
4089 if (!wiphy->bands[band])
4090 continue;
4091 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01004092 struct ieee80211_channel *chan;
4093
4094 chan = &wiphy->bands[band]->channels[j];
4095
4096 if (chan->flags & IEEE80211_CHAN_DISABLED)
4097 continue;
4098
4099 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01004100 i++;
4101 }
4102 }
4103 }
4104
Johannes Berg584991d2009-11-02 13:32:03 +01004105 if (!i) {
4106 err = -EINVAL;
4107 goto out_free;
4108 }
4109
4110 request->n_channels = i;
4111
Johannes Berg2a519312009-02-10 21:25:55 +01004112 i = 0;
4113 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4114 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004115 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01004116 err = -EINVAL;
4117 goto out_free;
4118 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004119 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01004120 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01004121 i++;
4122 }
4123 }
4124
Jouni Malinen70692ad2009-02-16 19:39:13 +02004125 if (info->attrs[NL80211_ATTR_IE]) {
4126 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02004127 memcpy((void *)request->ie,
4128 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02004129 request->ie_len);
4130 }
4131
Johannes Berg34850ab2011-07-18 18:08:35 +02004132 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02004133 if (wiphy->bands[i])
4134 request->rates[i] =
4135 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02004136
4137 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
4138 nla_for_each_nested(attr,
4139 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
4140 tmp) {
4141 enum ieee80211_band band = nla_type(attr);
4142
Dan Carpenter84404622011-07-29 11:52:18 +03004143 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02004144 err = -EINVAL;
4145 goto out_free;
4146 }
4147 err = ieee80211_get_ratemask(wiphy->bands[band],
4148 nla_data(attr),
4149 nla_len(attr),
4150 &request->rates[band]);
4151 if (err)
4152 goto out_free;
4153 }
4154 }
4155
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05304156 request->no_cck =
4157 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
4158
Johannes Berg463d0182009-07-14 00:33:35 +02004159 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02004160 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01004161
Johannes Berg79c97e92009-07-07 03:56:12 +02004162 rdev->scan_req = request;
4163 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01004164
Johannes Berg463d0182009-07-14 00:33:35 +02004165 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02004166 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02004167 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02004168 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01004169 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02004170 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01004171 kfree(request);
4172 }
Johannes Berg3b858752009-03-12 09:55:09 +01004173
Johannes Berg2a519312009-02-10 21:25:55 +01004174 return err;
4175}
4176
Luciano Coelho807f8a82011-05-11 17:09:35 +03004177static int nl80211_start_sched_scan(struct sk_buff *skb,
4178 struct genl_info *info)
4179{
4180 struct cfg80211_sched_scan_request *request;
4181 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4182 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004183 struct nlattr *attr;
4184 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004185 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004186 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004187 enum ieee80211_band band;
4188 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004189 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004190
4191 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4192 !rdev->ops->sched_scan_start)
4193 return -EOPNOTSUPP;
4194
4195 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4196 return -EINVAL;
4197
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004198 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
4199 return -EINVAL;
4200
4201 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
4202 if (interval == 0)
4203 return -EINVAL;
4204
Luciano Coelho807f8a82011-05-11 17:09:35 +03004205 wiphy = &rdev->wiphy;
4206
4207 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4208 n_channels = validate_scan_freqs(
4209 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
4210 if (!n_channels)
4211 return -EINVAL;
4212 } else {
4213 n_channels = 0;
4214
4215 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
4216 if (wiphy->bands[band])
4217 n_channels += wiphy->bands[band]->n_channels;
4218 }
4219
4220 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
4221 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4222 tmp)
4223 n_ssids++;
4224
Luciano Coelho93b6aa62011-07-13 14:57:28 +03004225 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004226 return -EINVAL;
4227
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004228 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
4229 nla_for_each_nested(attr,
4230 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4231 tmp)
4232 n_match_sets++;
4233
4234 if (n_match_sets > wiphy->max_match_sets)
4235 return -EINVAL;
4236
Luciano Coelho807f8a82011-05-11 17:09:35 +03004237 if (info->attrs[NL80211_ATTR_IE])
4238 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4239 else
4240 ie_len = 0;
4241
Luciano Coelho5a865ba2011-07-13 14:57:29 +03004242 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004243 return -EINVAL;
4244
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004245 mutex_lock(&rdev->sched_scan_mtx);
4246
4247 if (rdev->sched_scan_req) {
4248 err = -EINPROGRESS;
4249 goto out;
4250 }
4251
Luciano Coelho807f8a82011-05-11 17:09:35 +03004252 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004253 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004254 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004255 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03004256 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004257 if (!request) {
4258 err = -ENOMEM;
4259 goto out;
4260 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03004261
4262 if (n_ssids)
4263 request->ssids = (void *)&request->channels[n_channels];
4264 request->n_ssids = n_ssids;
4265 if (ie_len) {
4266 if (request->ssids)
4267 request->ie = (void *)(request->ssids + n_ssids);
4268 else
4269 request->ie = (void *)(request->channels + n_channels);
4270 }
4271
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004272 if (n_match_sets) {
4273 if (request->ie)
4274 request->match_sets = (void *)(request->ie + ie_len);
4275 else if (request->ssids)
4276 request->match_sets =
4277 (void *)(request->ssids + n_ssids);
4278 else
4279 request->match_sets =
4280 (void *)(request->channels + n_channels);
4281 }
4282 request->n_match_sets = n_match_sets;
4283
Luciano Coelho807f8a82011-05-11 17:09:35 +03004284 i = 0;
4285 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4286 /* user specified, bail out if channel not found */
4287 nla_for_each_nested(attr,
4288 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
4289 tmp) {
4290 struct ieee80211_channel *chan;
4291
4292 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
4293
4294 if (!chan) {
4295 err = -EINVAL;
4296 goto out_free;
4297 }
4298
4299 /* ignore disabled channels */
4300 if (chan->flags & IEEE80211_CHAN_DISABLED)
4301 continue;
4302
4303 request->channels[i] = chan;
4304 i++;
4305 }
4306 } else {
4307 /* all channels */
4308 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4309 int j;
4310 if (!wiphy->bands[band])
4311 continue;
4312 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
4313 struct ieee80211_channel *chan;
4314
4315 chan = &wiphy->bands[band]->channels[j];
4316
4317 if (chan->flags & IEEE80211_CHAN_DISABLED)
4318 continue;
4319
4320 request->channels[i] = chan;
4321 i++;
4322 }
4323 }
4324 }
4325
4326 if (!i) {
4327 err = -EINVAL;
4328 goto out_free;
4329 }
4330
4331 request->n_channels = i;
4332
4333 i = 0;
4334 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4335 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4336 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004337 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03004338 err = -EINVAL;
4339 goto out_free;
4340 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004341 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004342 memcpy(request->ssids[i].ssid, nla_data(attr),
4343 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03004344 i++;
4345 }
4346 }
4347
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004348 i = 0;
4349 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
4350 nla_for_each_nested(attr,
4351 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4352 tmp) {
4353 struct nlattr *ssid;
4354
4355 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
4356 nla_data(attr), nla_len(attr),
4357 nl80211_match_policy);
Johannes Berg4a4ab0d2012-06-13 11:17:11 +02004358 ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004359 if (ssid) {
4360 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
4361 err = -EINVAL;
4362 goto out_free;
4363 }
4364 memcpy(request->match_sets[i].ssid.ssid,
4365 nla_data(ssid), nla_len(ssid));
4366 request->match_sets[i].ssid.ssid_len =
4367 nla_len(ssid);
4368 }
4369 i++;
4370 }
4371 }
4372
Luciano Coelho807f8a82011-05-11 17:09:35 +03004373 if (info->attrs[NL80211_ATTR_IE]) {
4374 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4375 memcpy((void *)request->ie,
4376 nla_data(info->attrs[NL80211_ATTR_IE]),
4377 request->ie_len);
4378 }
4379
4380 request->dev = dev;
4381 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004382 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004383
4384 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
4385 if (!err) {
4386 rdev->sched_scan_req = request;
4387 nl80211_send_sched_scan(rdev, dev,
4388 NL80211_CMD_START_SCHED_SCAN);
4389 goto out;
4390 }
4391
4392out_free:
4393 kfree(request);
4394out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004395 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004396 return err;
4397}
4398
4399static int nl80211_stop_sched_scan(struct sk_buff *skb,
4400 struct genl_info *info)
4401{
4402 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004403 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004404
4405 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4406 !rdev->ops->sched_scan_stop)
4407 return -EOPNOTSUPP;
4408
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004409 mutex_lock(&rdev->sched_scan_mtx);
4410 err = __cfg80211_stop_sched_scan(rdev, false);
4411 mutex_unlock(&rdev->sched_scan_mtx);
4412
4413 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004414}
4415
Johannes Berg9720bb32011-06-21 09:45:33 +02004416static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
4417 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004418 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02004419 struct wireless_dev *wdev,
4420 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01004421{
Johannes Berg48ab9052009-07-10 18:42:31 +02004422 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01004423 void *hdr;
4424 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02004425
4426 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004427
Johannes Berg9720bb32011-06-21 09:45:33 +02004428 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004429 NL80211_CMD_NEW_SCAN_RESULTS);
4430 if (!hdr)
4431 return -1;
4432
Johannes Berg9720bb32011-06-21 09:45:33 +02004433 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
4434
David S. Miller9360ffd2012-03-29 04:41:26 -04004435 if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation) ||
4436 nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
4437 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004438
4439 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
4440 if (!bss)
4441 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04004442 if ((!is_zero_ether_addr(res->bssid) &&
4443 nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) ||
4444 (res->information_elements && res->len_information_elements &&
4445 nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
4446 res->len_information_elements,
4447 res->information_elements)) ||
4448 (res->beacon_ies && res->len_beacon_ies &&
4449 res->beacon_ies != res->information_elements &&
4450 nla_put(msg, NL80211_BSS_BEACON_IES,
4451 res->len_beacon_ies, res->beacon_ies)))
4452 goto nla_put_failure;
4453 if (res->tsf &&
4454 nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
4455 goto nla_put_failure;
4456 if (res->beacon_interval &&
4457 nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))
4458 goto nla_put_failure;
4459 if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
4460 nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
4461 nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
4462 jiffies_to_msecs(jiffies - intbss->ts)))
4463 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004464
Johannes Berg77965c92009-02-18 18:45:06 +01004465 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01004466 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04004467 if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
4468 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004469 break;
4470 case CFG80211_SIGNAL_TYPE_UNSPEC:
David S. Miller9360ffd2012-03-29 04:41:26 -04004471 if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal))
4472 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004473 break;
4474 default:
4475 break;
4476 }
4477
Johannes Berg48ab9052009-07-10 18:42:31 +02004478 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02004479 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02004480 case NL80211_IFTYPE_STATION:
David S. Miller9360ffd2012-03-29 04:41:26 -04004481 if (intbss == wdev->current_bss &&
4482 nla_put_u32(msg, NL80211_BSS_STATUS,
4483 NL80211_BSS_STATUS_ASSOCIATED))
4484 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02004485 break;
4486 case NL80211_IFTYPE_ADHOC:
David S. Miller9360ffd2012-03-29 04:41:26 -04004487 if (intbss == wdev->current_bss &&
4488 nla_put_u32(msg, NL80211_BSS_STATUS,
4489 NL80211_BSS_STATUS_IBSS_JOINED))
4490 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02004491 break;
4492 default:
4493 break;
4494 }
4495
Johannes Berg2a519312009-02-10 21:25:55 +01004496 nla_nest_end(msg, bss);
4497
4498 return genlmsg_end(msg, hdr);
4499
4500 nla_put_failure:
4501 genlmsg_cancel(msg, hdr);
4502 return -EMSGSIZE;
4503}
4504
4505static int nl80211_dump_scan(struct sk_buff *skb,
4506 struct netlink_callback *cb)
4507{
Johannes Berg48ab9052009-07-10 18:42:31 +02004508 struct cfg80211_registered_device *rdev;
4509 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01004510 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02004511 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01004512 int start = cb->args[1], idx = 0;
4513 int err;
4514
Johannes Berg67748892010-10-04 21:14:06 +02004515 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
4516 if (err)
4517 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01004518
Johannes Berg48ab9052009-07-10 18:42:31 +02004519 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01004520
Johannes Berg48ab9052009-07-10 18:42:31 +02004521 wdev_lock(wdev);
4522 spin_lock_bh(&rdev->bss_lock);
4523 cfg80211_bss_expire(rdev);
4524
Johannes Berg9720bb32011-06-21 09:45:33 +02004525 cb->seq = rdev->bss_generation;
4526
Johannes Berg48ab9052009-07-10 18:42:31 +02004527 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01004528 if (++idx <= start)
4529 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02004530 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01004531 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02004532 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004533 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02004534 break;
Johannes Berg2a519312009-02-10 21:25:55 +01004535 }
4536 }
4537
Johannes Berg48ab9052009-07-10 18:42:31 +02004538 spin_unlock_bh(&rdev->bss_lock);
4539 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004540
4541 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02004542 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004543
Johannes Berg67748892010-10-04 21:14:06 +02004544 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01004545}
4546
Holger Schurig61fa7132009-11-11 12:25:40 +01004547static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
4548 int flags, struct net_device *dev,
4549 struct survey_info *survey)
4550{
4551 void *hdr;
4552 struct nlattr *infoattr;
4553
Holger Schurig61fa7132009-11-11 12:25:40 +01004554 hdr = nl80211hdr_put(msg, pid, seq, flags,
4555 NL80211_CMD_NEW_SURVEY_RESULTS);
4556 if (!hdr)
4557 return -ENOMEM;
4558
David S. Miller9360ffd2012-03-29 04:41:26 -04004559 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
4560 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01004561
4562 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
4563 if (!infoattr)
4564 goto nla_put_failure;
4565
David S. Miller9360ffd2012-03-29 04:41:26 -04004566 if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
4567 survey->channel->center_freq))
4568 goto nla_put_failure;
4569
4570 if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
4571 nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
4572 goto nla_put_failure;
4573 if ((survey->filled & SURVEY_INFO_IN_USE) &&
4574 nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
4575 goto nla_put_failure;
4576 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
4577 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
4578 survey->channel_time))
4579 goto nla_put_failure;
4580 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
4581 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
4582 survey->channel_time_busy))
4583 goto nla_put_failure;
4584 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
4585 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
4586 survey->channel_time_ext_busy))
4587 goto nla_put_failure;
4588 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
4589 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
4590 survey->channel_time_rx))
4591 goto nla_put_failure;
4592 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
4593 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
4594 survey->channel_time_tx))
4595 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01004596
4597 nla_nest_end(msg, infoattr);
4598
4599 return genlmsg_end(msg, hdr);
4600
4601 nla_put_failure:
4602 genlmsg_cancel(msg, hdr);
4603 return -EMSGSIZE;
4604}
4605
4606static int nl80211_dump_survey(struct sk_buff *skb,
4607 struct netlink_callback *cb)
4608{
4609 struct survey_info survey;
4610 struct cfg80211_registered_device *dev;
4611 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01004612 int survey_idx = cb->args[1];
4613 int res;
4614
Johannes Berg67748892010-10-04 21:14:06 +02004615 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
4616 if (res)
4617 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01004618
4619 if (!dev->ops->dump_survey) {
4620 res = -EOPNOTSUPP;
4621 goto out_err;
4622 }
4623
4624 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004625 struct ieee80211_channel *chan;
4626
Holger Schurig61fa7132009-11-11 12:25:40 +01004627 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
4628 &survey);
4629 if (res == -ENOENT)
4630 break;
4631 if (res)
4632 goto out_err;
4633
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004634 /* Survey without a channel doesn't make sense */
4635 if (!survey.channel) {
4636 res = -EINVAL;
4637 goto out;
4638 }
4639
4640 chan = ieee80211_get_channel(&dev->wiphy,
4641 survey.channel->center_freq);
4642 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
4643 survey_idx++;
4644 continue;
4645 }
4646
Holger Schurig61fa7132009-11-11 12:25:40 +01004647 if (nl80211_send_survey(skb,
4648 NETLINK_CB(cb->skb).pid,
4649 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4650 netdev,
4651 &survey) < 0)
4652 goto out;
4653 survey_idx++;
4654 }
4655
4656 out:
4657 cb->args[1] = survey_idx;
4658 res = skb->len;
4659 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02004660 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01004661 return res;
4662}
4663
Jouni Malinen255e7372009-03-20 21:21:17 +02004664static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
4665{
Samuel Ortizb23aa672009-07-01 21:26:54 +02004666 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02004667}
4668
Samuel Ortizb23aa672009-07-01 21:26:54 +02004669static bool nl80211_valid_wpa_versions(u32 wpa_versions)
4670{
4671 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
4672 NL80211_WPA_VERSION_2));
4673}
4674
Jouni Malinen636a5d32009-03-19 13:39:22 +02004675static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
4676{
Johannes Berg4c476992010-10-04 21:36:35 +02004677 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4678 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004679 struct ieee80211_channel *chan;
4680 const u8 *bssid, *ssid, *ie = NULL;
4681 int err, ssid_len, ie_len = 0;
4682 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02004683 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004684 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004685
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004686 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4687 return -EINVAL;
4688
4689 if (!info->attrs[NL80211_ATTR_MAC])
4690 return -EINVAL;
4691
Jouni Malinen17780922009-03-27 20:52:47 +02004692 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4693 return -EINVAL;
4694
Johannes Berg19957bb2009-07-02 17:20:43 +02004695 if (!info->attrs[NL80211_ATTR_SSID])
4696 return -EINVAL;
4697
4698 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4699 return -EINVAL;
4700
Johannes Bergfffd0932009-07-08 14:22:54 +02004701 err = nl80211_parse_key(info, &key);
4702 if (err)
4703 return err;
4704
4705 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004706 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4707 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004708 if (!key.p.key || !key.p.key_len)
4709 return -EINVAL;
4710 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4711 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4712 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4713 key.p.key_len != WLAN_KEY_LEN_WEP104))
4714 return -EINVAL;
4715 if (key.idx > 4)
4716 return -EINVAL;
4717 } else {
4718 key.p.key_len = 0;
4719 key.p.key = NULL;
4720 }
4721
Johannes Bergafea0b72010-08-10 09:46:42 +02004722 if (key.idx >= 0) {
4723 int i;
4724 bool ok = false;
4725 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4726 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4727 ok = true;
4728 break;
4729 }
4730 }
Johannes Berg4c476992010-10-04 21:36:35 +02004731 if (!ok)
4732 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004733 }
4734
Johannes Berg4c476992010-10-04 21:36:35 +02004735 if (!rdev->ops->auth)
4736 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004737
Johannes Berg074ac8d2010-09-16 14:58:22 +02004738 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004739 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4740 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004741
Johannes Berg19957bb2009-07-02 17:20:43 +02004742 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004743 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004744 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004745 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4746 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004747
Johannes Berg19957bb2009-07-02 17:20:43 +02004748 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4749 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4750
4751 if (info->attrs[NL80211_ATTR_IE]) {
4752 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4753 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4754 }
4755
4756 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004757 if (!nl80211_valid_auth_type(auth_type))
4758 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004759
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004760 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4761
Johannes Berg95de8172012-01-20 13:55:25 +01004762 /*
4763 * Since we no longer track auth state, ignore
4764 * requests to only change local state.
4765 */
4766 if (local_state_change)
4767 return 0;
4768
Johannes Berg4c476992010-10-04 21:36:35 +02004769 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4770 ssid, ssid_len, ie, ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +01004771 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004772}
4773
Johannes Bergc0692b82010-08-27 14:26:53 +03004774static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4775 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004776 struct cfg80211_crypto_settings *settings,
4777 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004778{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004779 memset(settings, 0, sizeof(*settings));
4780
Samuel Ortizb23aa672009-07-01 21:26:54 +02004781 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4782
Johannes Bergc0692b82010-08-27 14:26:53 +03004783 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4784 u16 proto;
4785 proto = nla_get_u16(
4786 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4787 settings->control_port_ethertype = cpu_to_be16(proto);
4788 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4789 proto != ETH_P_PAE)
4790 return -EINVAL;
4791 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4792 settings->control_port_no_encrypt = true;
4793 } else
4794 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4795
Samuel Ortizb23aa672009-07-01 21:26:54 +02004796 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4797 void *data;
4798 int len, i;
4799
4800 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4801 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4802 settings->n_ciphers_pairwise = len / sizeof(u32);
4803
4804 if (len % sizeof(u32))
4805 return -EINVAL;
4806
Johannes Berg3dc27d22009-07-02 21:36:37 +02004807 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004808 return -EINVAL;
4809
4810 memcpy(settings->ciphers_pairwise, data, len);
4811
4812 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004813 if (!cfg80211_supported_cipher_suite(
4814 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004815 settings->ciphers_pairwise[i]))
4816 return -EINVAL;
4817 }
4818
4819 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4820 settings->cipher_group =
4821 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004822 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
4823 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02004824 return -EINVAL;
4825 }
4826
4827 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4828 settings->wpa_versions =
4829 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4830 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4831 return -EINVAL;
4832 }
4833
4834 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4835 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03004836 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004837
4838 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4839 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4840 settings->n_akm_suites = len / sizeof(u32);
4841
4842 if (len % sizeof(u32))
4843 return -EINVAL;
4844
Jouni Malinen1b9ca022011-09-21 16:13:07 +03004845 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4846 return -EINVAL;
4847
Samuel Ortizb23aa672009-07-01 21:26:54 +02004848 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004849 }
4850
4851 return 0;
4852}
4853
Jouni Malinen636a5d32009-03-19 13:39:22 +02004854static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4855{
Johannes Berg4c476992010-10-04 21:36:35 +02004856 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4857 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004858 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004859 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004860 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004861 int err, ssid_len, ie_len = 0;
4862 bool use_mfp = false;
Ben Greear7e7c8922011-11-18 11:31:59 -08004863 u32 flags = 0;
4864 struct ieee80211_ht_cap *ht_capa = NULL;
4865 struct ieee80211_ht_cap *ht_capa_mask = NULL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004866
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004867 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4868 return -EINVAL;
4869
4870 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004871 !info->attrs[NL80211_ATTR_SSID] ||
4872 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004873 return -EINVAL;
4874
Johannes Berg4c476992010-10-04 21:36:35 +02004875 if (!rdev->ops->assoc)
4876 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004877
Johannes Berg074ac8d2010-09-16 14:58:22 +02004878 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004879 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4880 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004881
Johannes Berg19957bb2009-07-02 17:20:43 +02004882 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004883
Johannes Berg19957bb2009-07-02 17:20:43 +02004884 chan = ieee80211_get_channel(&rdev->wiphy,
4885 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004886 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4887 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004888
Johannes Berg19957bb2009-07-02 17:20:43 +02004889 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4890 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004891
4892 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004893 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4894 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004895 }
4896
Jouni Malinendc6382c2009-05-06 22:09:37 +03004897 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004898 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004899 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004900 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004901 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004902 else if (mfp != NL80211_MFP_NO)
4903 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004904 }
4905
Johannes Berg3e5d7642009-07-07 14:37:26 +02004906 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4907 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4908
Ben Greear7e7c8922011-11-18 11:31:59 -08004909 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
4910 flags |= ASSOC_REQ_DISABLE_HT;
4911
4912 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
4913 ht_capa_mask =
4914 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
4915
4916 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
4917 if (!ht_capa_mask)
4918 return -EINVAL;
4919 ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
4920 }
4921
Johannes Bergc0692b82010-08-27 14:26:53 +03004922 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004923 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004924 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4925 ssid, ssid_len, ie, ie_len, use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -08004926 &crypto, flags, ht_capa,
4927 ht_capa_mask);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004928
Jouni Malinen636a5d32009-03-19 13:39:22 +02004929 return err;
4930}
4931
4932static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4933{
Johannes Berg4c476992010-10-04 21:36:35 +02004934 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4935 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004936 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004937 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004938 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004939 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004940
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004941 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4942 return -EINVAL;
4943
4944 if (!info->attrs[NL80211_ATTR_MAC])
4945 return -EINVAL;
4946
4947 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4948 return -EINVAL;
4949
Johannes Berg4c476992010-10-04 21:36:35 +02004950 if (!rdev->ops->deauth)
4951 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004952
Johannes Berg074ac8d2010-09-16 14:58:22 +02004953 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004954 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4955 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004956
Johannes Berg19957bb2009-07-02 17:20:43 +02004957 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004958
Johannes Berg19957bb2009-07-02 17:20:43 +02004959 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4960 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004961 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004962 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004963 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004964
4965 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004966 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4967 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004968 }
4969
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004970 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4971
Johannes Berg4c476992010-10-04 21:36:35 +02004972 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4973 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004974}
4975
4976static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4977{
Johannes Berg4c476992010-10-04 21:36:35 +02004978 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4979 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004980 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004981 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004982 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004983 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004984
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004985 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4986 return -EINVAL;
4987
4988 if (!info->attrs[NL80211_ATTR_MAC])
4989 return -EINVAL;
4990
4991 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4992 return -EINVAL;
4993
Johannes Berg4c476992010-10-04 21:36:35 +02004994 if (!rdev->ops->disassoc)
4995 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004996
Johannes Berg074ac8d2010-09-16 14:58:22 +02004997 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004998 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4999 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02005000
Johannes Berg19957bb2009-07-02 17:20:43 +02005001 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005002
Johannes Berg19957bb2009-07-02 17:20:43 +02005003 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5004 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01005005 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02005006 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02005007 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02005008
5009 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02005010 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5011 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005012 }
5013
Jouni Malinend5cdfac2010-04-04 09:37:19 +03005014 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
5015
Johannes Berg4c476992010-10-04 21:36:35 +02005016 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
5017 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02005018}
5019
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01005020static bool
5021nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
5022 int mcast_rate[IEEE80211_NUM_BANDS],
5023 int rateval)
5024{
5025 struct wiphy *wiphy = &rdev->wiphy;
5026 bool found = false;
5027 int band, i;
5028
5029 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
5030 struct ieee80211_supported_band *sband;
5031
5032 sband = wiphy->bands[band];
5033 if (!sband)
5034 continue;
5035
5036 for (i = 0; i < sband->n_bitrates; i++) {
5037 if (sband->bitrates[i].bitrate == rateval) {
5038 mcast_rate[band] = i + 1;
5039 found = true;
5040 break;
5041 }
5042 }
5043 }
5044
5045 return found;
5046}
5047
Johannes Berg04a773a2009-04-19 21:24:32 +02005048static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
5049{
Johannes Berg4c476992010-10-04 21:36:35 +02005050 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5051 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02005052 struct cfg80211_ibss_params ibss;
5053 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02005054 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02005055 int err;
5056
Johannes Berg8e30bc52009-04-22 17:45:38 +02005057 memset(&ibss, 0, sizeof(ibss));
5058
Johannes Berg04a773a2009-04-19 21:24:32 +02005059 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5060 return -EINVAL;
5061
5062 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5063 !info->attrs[NL80211_ATTR_SSID] ||
5064 !nla_len(info->attrs[NL80211_ATTR_SSID]))
5065 return -EINVAL;
5066
Johannes Berg8e30bc52009-04-22 17:45:38 +02005067 ibss.beacon_interval = 100;
5068
5069 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
5070 ibss.beacon_interval =
5071 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
5072 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
5073 return -EINVAL;
5074 }
5075
Johannes Berg4c476992010-10-04 21:36:35 +02005076 if (!rdev->ops->join_ibss)
5077 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005078
Johannes Berg4c476992010-10-04 21:36:35 +02005079 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
5080 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005081
Johannes Berg79c97e92009-07-07 03:56:12 +02005082 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02005083
Johannes Berg39193492011-09-16 13:45:25 +02005084 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02005085 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02005086
5087 if (!is_valid_ether_addr(ibss.bssid))
5088 return -EINVAL;
5089 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005090 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5091 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5092
5093 if (info->attrs[NL80211_ATTR_IE]) {
5094 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5095 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5096 }
5097
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005098 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5099 enum nl80211_channel_type channel_type;
5100
Johannes Bergcd6c6592012-05-10 21:27:18 +02005101 if (!nl80211_valid_channel_type(info, &channel_type))
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005102 return -EINVAL;
5103
5104 if (channel_type != NL80211_CHAN_NO_HT &&
5105 !(wiphy->features & NL80211_FEATURE_HT_IBSS))
5106 return -EINVAL;
5107
5108 ibss.channel_type = channel_type;
5109 } else {
5110 ibss.channel_type = NL80211_CHAN_NO_HT;
5111 }
5112
5113 ibss.channel = rdev_freq_to_chan(rdev,
5114 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
5115 ibss.channel_type);
Johannes Berg04a773a2009-04-19 21:24:32 +02005116 if (!ibss.channel ||
5117 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02005118 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
5119 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02005120
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005121 /* Both channels should be able to initiate communication */
5122 if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
5123 ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
5124 !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
5125 ibss.channel_type))
5126 return -EINVAL;
5127
Johannes Berg04a773a2009-04-19 21:24:32 +02005128 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02005129 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02005130
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005131 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
5132 u8 *rates =
5133 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5134 int n_rates =
5135 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5136 struct ieee80211_supported_band *sband =
5137 wiphy->bands[ibss.channel->band];
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005138
Johannes Berg34850ab2011-07-18 18:08:35 +02005139 err = ieee80211_get_ratemask(sband, rates, n_rates,
5140 &ibss.basic_rates);
5141 if (err)
5142 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005143 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01005144
5145 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
5146 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
5147 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
5148 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005149
Johannes Berg4c476992010-10-04 21:36:35 +02005150 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5151 connkeys = nl80211_parse_connkeys(rdev,
5152 info->attrs[NL80211_ATTR_KEYS]);
5153 if (IS_ERR(connkeys))
5154 return PTR_ERR(connkeys);
5155 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005156
Antonio Quartulli267335d2012-01-31 20:25:47 +01005157 ibss.control_port =
5158 nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
5159
Johannes Berg4c476992010-10-04 21:36:35 +02005160 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005161 if (err)
5162 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02005163 return err;
5164}
5165
5166static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
5167{
Johannes Berg4c476992010-10-04 21:36:35 +02005168 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5169 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02005170
Johannes Berg4c476992010-10-04 21:36:35 +02005171 if (!rdev->ops->leave_ibss)
5172 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005173
Johannes Berg4c476992010-10-04 21:36:35 +02005174 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
5175 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005176
Johannes Berg4c476992010-10-04 21:36:35 +02005177 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02005178}
5179
Johannes Bergaff89a92009-07-01 21:26:51 +02005180#ifdef CONFIG_NL80211_TESTMODE
5181static struct genl_multicast_group nl80211_testmode_mcgrp = {
5182 .name = "testmode",
5183};
5184
5185static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
5186{
Johannes Berg4c476992010-10-04 21:36:35 +02005187 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02005188 int err;
5189
5190 if (!info->attrs[NL80211_ATTR_TESTDATA])
5191 return -EINVAL;
5192
Johannes Bergaff89a92009-07-01 21:26:51 +02005193 err = -EOPNOTSUPP;
5194 if (rdev->ops->testmode_cmd) {
5195 rdev->testmode_info = info;
5196 err = rdev->ops->testmode_cmd(&rdev->wiphy,
5197 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
5198 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
5199 rdev->testmode_info = NULL;
5200 }
5201
Johannes Bergaff89a92009-07-01 21:26:51 +02005202 return err;
5203}
5204
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005205static int nl80211_testmode_dump(struct sk_buff *skb,
5206 struct netlink_callback *cb)
5207{
Johannes Berg00918d32011-12-13 17:22:05 +01005208 struct cfg80211_registered_device *rdev;
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005209 int err;
5210 long phy_idx;
5211 void *data = NULL;
5212 int data_len = 0;
5213
5214 if (cb->args[0]) {
5215 /*
5216 * 0 is a valid index, but not valid for args[0],
5217 * so we need to offset by 1.
5218 */
5219 phy_idx = cb->args[0] - 1;
5220 } else {
5221 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
5222 nl80211_fam.attrbuf, nl80211_fam.maxattr,
5223 nl80211_policy);
5224 if (err)
5225 return err;
Johannes Berg00918d32011-12-13 17:22:05 +01005226 if (nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) {
5227 phy_idx = nla_get_u32(
5228 nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
5229 } else {
5230 struct net_device *netdev;
5231
5232 err = get_rdev_dev_by_ifindex(sock_net(skb->sk),
5233 nl80211_fam.attrbuf,
5234 &rdev, &netdev);
5235 if (err)
5236 return err;
5237 dev_put(netdev);
5238 phy_idx = rdev->wiphy_idx;
5239 cfg80211_unlock_rdev(rdev);
5240 }
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005241 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
5242 cb->args[1] =
5243 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
5244 }
5245
5246 if (cb->args[1]) {
5247 data = nla_data((void *)cb->args[1]);
5248 data_len = nla_len((void *)cb->args[1]);
5249 }
5250
5251 mutex_lock(&cfg80211_mutex);
Johannes Berg00918d32011-12-13 17:22:05 +01005252 rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
5253 if (!rdev) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005254 mutex_unlock(&cfg80211_mutex);
5255 return -ENOENT;
5256 }
Johannes Berg00918d32011-12-13 17:22:05 +01005257 cfg80211_lock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005258 mutex_unlock(&cfg80211_mutex);
5259
Johannes Berg00918d32011-12-13 17:22:05 +01005260 if (!rdev->ops->testmode_dump) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005261 err = -EOPNOTSUPP;
5262 goto out_err;
5263 }
5264
5265 while (1) {
5266 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
5267 cb->nlh->nlmsg_seq, NLM_F_MULTI,
5268 NL80211_CMD_TESTMODE);
5269 struct nlattr *tmdata;
5270
David S. Miller9360ffd2012-03-29 04:41:26 -04005271 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005272 genlmsg_cancel(skb, hdr);
5273 break;
5274 }
5275
5276 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5277 if (!tmdata) {
5278 genlmsg_cancel(skb, hdr);
5279 break;
5280 }
Johannes Berg00918d32011-12-13 17:22:05 +01005281 err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb,
5282 data, data_len);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005283 nla_nest_end(skb, tmdata);
5284
5285 if (err == -ENOBUFS || err == -ENOENT) {
5286 genlmsg_cancel(skb, hdr);
5287 break;
5288 } else if (err) {
5289 genlmsg_cancel(skb, hdr);
5290 goto out_err;
5291 }
5292
5293 genlmsg_end(skb, hdr);
5294 }
5295
5296 err = skb->len;
5297 /* see above */
5298 cb->args[0] = phy_idx + 1;
5299 out_err:
Johannes Berg00918d32011-12-13 17:22:05 +01005300 cfg80211_unlock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005301 return err;
5302}
5303
Johannes Bergaff89a92009-07-01 21:26:51 +02005304static struct sk_buff *
5305__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
5306 int approxlen, u32 pid, u32 seq, gfp_t gfp)
5307{
5308 struct sk_buff *skb;
5309 void *hdr;
5310 struct nlattr *data;
5311
5312 skb = nlmsg_new(approxlen + 100, gfp);
5313 if (!skb)
5314 return NULL;
5315
5316 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
5317 if (!hdr) {
5318 kfree_skb(skb);
5319 return NULL;
5320 }
5321
David S. Miller9360ffd2012-03-29 04:41:26 -04005322 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
5323 goto nla_put_failure;
Johannes Bergaff89a92009-07-01 21:26:51 +02005324 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5325
5326 ((void **)skb->cb)[0] = rdev;
5327 ((void **)skb->cb)[1] = hdr;
5328 ((void **)skb->cb)[2] = data;
5329
5330 return skb;
5331
5332 nla_put_failure:
5333 kfree_skb(skb);
5334 return NULL;
5335}
5336
5337struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
5338 int approxlen)
5339{
5340 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5341
5342 if (WARN_ON(!rdev->testmode_info))
5343 return NULL;
5344
5345 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
5346 rdev->testmode_info->snd_pid,
5347 rdev->testmode_info->snd_seq,
5348 GFP_KERNEL);
5349}
5350EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
5351
5352int cfg80211_testmode_reply(struct sk_buff *skb)
5353{
5354 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
5355 void *hdr = ((void **)skb->cb)[1];
5356 struct nlattr *data = ((void **)skb->cb)[2];
5357
5358 if (WARN_ON(!rdev->testmode_info)) {
5359 kfree_skb(skb);
5360 return -EINVAL;
5361 }
5362
5363 nla_nest_end(skb, data);
5364 genlmsg_end(skb, hdr);
5365 return genlmsg_reply(skb, rdev->testmode_info);
5366}
5367EXPORT_SYMBOL(cfg80211_testmode_reply);
5368
5369struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
5370 int approxlen, gfp_t gfp)
5371{
5372 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5373
5374 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
5375}
5376EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
5377
5378void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
5379{
5380 void *hdr = ((void **)skb->cb)[1];
5381 struct nlattr *data = ((void **)skb->cb)[2];
5382
5383 nla_nest_end(skb, data);
5384 genlmsg_end(skb, hdr);
5385 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
5386}
5387EXPORT_SYMBOL(cfg80211_testmode_event);
5388#endif
5389
Samuel Ortizb23aa672009-07-01 21:26:54 +02005390static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
5391{
Johannes Berg4c476992010-10-04 21:36:35 +02005392 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5393 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005394 struct cfg80211_connect_params connect;
5395 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02005396 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005397 int err;
5398
5399 memset(&connect, 0, sizeof(connect));
5400
5401 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5402 return -EINVAL;
5403
5404 if (!info->attrs[NL80211_ATTR_SSID] ||
5405 !nla_len(info->attrs[NL80211_ATTR_SSID]))
5406 return -EINVAL;
5407
5408 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
5409 connect.auth_type =
5410 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
5411 if (!nl80211_valid_auth_type(connect.auth_type))
5412 return -EINVAL;
5413 } else
5414 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
5415
5416 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
5417
Johannes Bergc0692b82010-08-27 14:26:53 +03005418 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02005419 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005420 if (err)
5421 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005422
Johannes Berg074ac8d2010-09-16 14:58:22 +02005423 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005424 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5425 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005426
Johannes Berg79c97e92009-07-07 03:56:12 +02005427 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005428
Bala Shanmugam4486ea92012-03-07 17:27:12 +05305429 connect.bg_scan_period = -1;
5430 if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
5431 (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
5432 connect.bg_scan_period =
5433 nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
5434 }
5435
Samuel Ortizb23aa672009-07-01 21:26:54 +02005436 if (info->attrs[NL80211_ATTR_MAC])
5437 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5438 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5439 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5440
5441 if (info->attrs[NL80211_ATTR_IE]) {
5442 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5443 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5444 }
5445
5446 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
5447 connect.channel =
5448 ieee80211_get_channel(wiphy,
5449 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
5450 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02005451 connect.channel->flags & IEEE80211_CHAN_DISABLED)
5452 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005453 }
5454
Johannes Bergfffd0932009-07-08 14:22:54 +02005455 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5456 connkeys = nl80211_parse_connkeys(rdev,
5457 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02005458 if (IS_ERR(connkeys))
5459 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005460 }
5461
Ben Greear7e7c8922011-11-18 11:31:59 -08005462 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
5463 connect.flags |= ASSOC_REQ_DISABLE_HT;
5464
5465 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5466 memcpy(&connect.ht_capa_mask,
5467 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
5468 sizeof(connect.ht_capa_mask));
5469
5470 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
5471 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5472 return -EINVAL;
5473 memcpy(&connect.ht_capa,
5474 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
5475 sizeof(connect.ht_capa));
5476 }
5477
Johannes Bergfffd0932009-07-08 14:22:54 +02005478 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005479 if (err)
5480 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005481 return err;
5482}
5483
5484static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
5485{
Johannes Berg4c476992010-10-04 21:36:35 +02005486 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5487 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005488 u16 reason;
5489
5490 if (!info->attrs[NL80211_ATTR_REASON_CODE])
5491 reason = WLAN_REASON_DEAUTH_LEAVING;
5492 else
5493 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5494
5495 if (reason == 0)
5496 return -EINVAL;
5497
Johannes Berg074ac8d2010-09-16 14:58:22 +02005498 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005499 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5500 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005501
Johannes Berg4c476992010-10-04 21:36:35 +02005502 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005503}
5504
Johannes Berg463d0182009-07-14 00:33:35 +02005505static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
5506{
Johannes Berg4c476992010-10-04 21:36:35 +02005507 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02005508 struct net *net;
5509 int err;
5510 u32 pid;
5511
5512 if (!info->attrs[NL80211_ATTR_PID])
5513 return -EINVAL;
5514
5515 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
5516
Johannes Berg463d0182009-07-14 00:33:35 +02005517 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02005518 if (IS_ERR(net))
5519 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005520
5521 err = 0;
5522
5523 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02005524 if (!net_eq(wiphy_net(&rdev->wiphy), net))
5525 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02005526
Johannes Berg463d0182009-07-14 00:33:35 +02005527 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005528 return err;
5529}
5530
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005531static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
5532{
Johannes Berg4c476992010-10-04 21:36:35 +02005533 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005534 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
5535 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02005536 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005537 struct cfg80211_pmksa pmksa;
5538
5539 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
5540
5541 if (!info->attrs[NL80211_ATTR_MAC])
5542 return -EINVAL;
5543
5544 if (!info->attrs[NL80211_ATTR_PMKID])
5545 return -EINVAL;
5546
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005547 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
5548 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5549
Johannes Berg074ac8d2010-09-16 14:58:22 +02005550 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005551 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5552 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005553
5554 switch (info->genlhdr->cmd) {
5555 case NL80211_CMD_SET_PMKSA:
5556 rdev_ops = rdev->ops->set_pmksa;
5557 break;
5558 case NL80211_CMD_DEL_PMKSA:
5559 rdev_ops = rdev->ops->del_pmksa;
5560 break;
5561 default:
5562 WARN_ON(1);
5563 break;
5564 }
5565
Johannes Berg4c476992010-10-04 21:36:35 +02005566 if (!rdev_ops)
5567 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005568
Johannes Berg4c476992010-10-04 21:36:35 +02005569 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005570}
5571
5572static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
5573{
Johannes Berg4c476992010-10-04 21:36:35 +02005574 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5575 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005576
Johannes Berg074ac8d2010-09-16 14:58:22 +02005577 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005578 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5579 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005580
Johannes Berg4c476992010-10-04 21:36:35 +02005581 if (!rdev->ops->flush_pmksa)
5582 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005583
Johannes Berg4c476992010-10-04 21:36:35 +02005584 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005585}
5586
Arik Nemtsov109086c2011-09-28 14:12:50 +03005587static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
5588{
5589 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5590 struct net_device *dev = info->user_ptr[1];
5591 u8 action_code, dialog_token;
5592 u16 status_code;
5593 u8 *peer;
5594
5595 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5596 !rdev->ops->tdls_mgmt)
5597 return -EOPNOTSUPP;
5598
5599 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
5600 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
5601 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
5602 !info->attrs[NL80211_ATTR_IE] ||
5603 !info->attrs[NL80211_ATTR_MAC])
5604 return -EINVAL;
5605
5606 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5607 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
5608 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
5609 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
5610
5611 return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
5612 dialog_token, status_code,
5613 nla_data(info->attrs[NL80211_ATTR_IE]),
5614 nla_len(info->attrs[NL80211_ATTR_IE]));
5615}
5616
5617static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
5618{
5619 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5620 struct net_device *dev = info->user_ptr[1];
5621 enum nl80211_tdls_operation operation;
5622 u8 *peer;
5623
5624 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5625 !rdev->ops->tdls_oper)
5626 return -EOPNOTSUPP;
5627
5628 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
5629 !info->attrs[NL80211_ATTR_MAC])
5630 return -EINVAL;
5631
5632 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
5633 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5634
5635 return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
5636}
5637
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005638static int nl80211_remain_on_channel(struct sk_buff *skb,
5639 struct genl_info *info)
5640{
Johannes Berg4c476992010-10-04 21:36:35 +02005641 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5642 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005643 struct ieee80211_channel *chan;
5644 struct sk_buff *msg;
5645 void *hdr;
5646 u64 cookie;
5647 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
5648 u32 freq, duration;
5649 int err;
5650
5651 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5652 !info->attrs[NL80211_ATTR_DURATION])
5653 return -EINVAL;
5654
5655 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5656
Johannes Berg7c4ef712011-11-18 15:33:48 +01005657 if (!rdev->ops->remain_on_channel ||
5658 !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
Johannes Berg4c476992010-10-04 21:36:35 +02005659 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005660
Johannes Bergebf348f2012-06-01 12:50:54 +02005661 /*
5662 * We should be on that channel for at least a minimum amount of
5663 * time (10ms) but no longer than the driver supports.
5664 */
5665 if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
5666 duration > rdev->wiphy.max_remain_on_channel_duration)
5667 return -EINVAL;
5668
Johannes Bergcd6c6592012-05-10 21:27:18 +02005669 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
5670 !nl80211_valid_channel_type(info, &channel_type))
5671 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005672
5673 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5674 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005675 if (chan == NULL)
5676 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005677
5678 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005679 if (!msg)
5680 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005681
5682 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5683 NL80211_CMD_REMAIN_ON_CHANNEL);
5684
5685 if (IS_ERR(hdr)) {
5686 err = PTR_ERR(hdr);
5687 goto free_msg;
5688 }
5689
5690 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
5691 channel_type, duration, &cookie);
5692
5693 if (err)
5694 goto free_msg;
5695
David S. Miller9360ffd2012-03-29 04:41:26 -04005696 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
5697 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005698
5699 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005700
5701 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005702
5703 nla_put_failure:
5704 err = -ENOBUFS;
5705 free_msg:
5706 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005707 return err;
5708}
5709
5710static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
5711 struct genl_info *info)
5712{
Johannes Berg4c476992010-10-04 21:36:35 +02005713 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5714 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005715 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005716
5717 if (!info->attrs[NL80211_ATTR_COOKIE])
5718 return -EINVAL;
5719
Johannes Berg4c476992010-10-04 21:36:35 +02005720 if (!rdev->ops->cancel_remain_on_channel)
5721 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005722
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005723 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5724
Johannes Berg4c476992010-10-04 21:36:35 +02005725 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005726}
5727
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005728static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
5729 u8 *rates, u8 rates_len)
5730{
5731 u8 i;
5732 u32 mask = 0;
5733
5734 for (i = 0; i < rates_len; i++) {
5735 int rate = (rates[i] & 0x7f) * 5;
5736 int ridx;
5737 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
5738 struct ieee80211_rate *srate =
5739 &sband->bitrates[ridx];
5740 if (rate == srate->bitrate) {
5741 mask |= 1 << ridx;
5742 break;
5743 }
5744 }
5745 if (ridx == sband->n_bitrates)
5746 return 0; /* rate not found */
5747 }
5748
5749 return mask;
5750}
5751
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005752static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
5753 u8 *rates, u8 rates_len,
5754 u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
5755{
5756 u8 i;
5757
5758 memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
5759
5760 for (i = 0; i < rates_len; i++) {
5761 int ridx, rbit;
5762
5763 ridx = rates[i] / 8;
5764 rbit = BIT(rates[i] % 8);
5765
5766 /* check validity */
Dan Carpenter910570b52012-02-01 10:42:11 +03005767 if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005768 return false;
5769
5770 /* check availability */
5771 if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
5772 mcs[ridx] |= rbit;
5773 else
5774 return false;
5775 }
5776
5777 return true;
5778}
5779
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00005780static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005781 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
5782 .len = NL80211_MAX_SUPP_RATES },
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005783 [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
5784 .len = NL80211_MAX_SUPP_HT_RATES },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005785};
5786
5787static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
5788 struct genl_info *info)
5789{
5790 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02005791 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005792 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02005793 int rem, i;
5794 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005795 struct nlattr *tx_rates;
5796 struct ieee80211_supported_band *sband;
5797
5798 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
5799 return -EINVAL;
5800
Johannes Berg4c476992010-10-04 21:36:35 +02005801 if (!rdev->ops->set_bitrate_mask)
5802 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005803
5804 memset(&mask, 0, sizeof(mask));
5805 /* Default to all rates enabled */
5806 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
5807 sband = rdev->wiphy.bands[i];
5808 mask.control[i].legacy =
5809 sband ? (1 << sband->n_bitrates) - 1 : 0;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005810 if (sband)
5811 memcpy(mask.control[i].mcs,
5812 sband->ht_cap.mcs.rx_mask,
5813 sizeof(mask.control[i].mcs));
5814 else
5815 memset(mask.control[i].mcs, 0,
5816 sizeof(mask.control[i].mcs));
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005817 }
5818
5819 /*
5820 * The nested attribute uses enum nl80211_band as the index. This maps
5821 * directly to the enum ieee80211_band values used in cfg80211.
5822 */
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005823 BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005824 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
5825 {
5826 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02005827 if (band < 0 || band >= IEEE80211_NUM_BANDS)
5828 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005829 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02005830 if (sband == NULL)
5831 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005832 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
5833 nla_len(tx_rates), nl80211_txattr_policy);
5834 if (tb[NL80211_TXRATE_LEGACY]) {
5835 mask.control[band].legacy = rateset_to_mask(
5836 sband,
5837 nla_data(tb[NL80211_TXRATE_LEGACY]),
5838 nla_len(tb[NL80211_TXRATE_LEGACY]));
Bala Shanmugam218d2e22012-04-20 19:12:58 +05305839 if ((mask.control[band].legacy == 0) &&
5840 nla_len(tb[NL80211_TXRATE_LEGACY]))
5841 return -EINVAL;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005842 }
5843 if (tb[NL80211_TXRATE_MCS]) {
5844 if (!ht_rateset_to_mask(
5845 sband,
5846 nla_data(tb[NL80211_TXRATE_MCS]),
5847 nla_len(tb[NL80211_TXRATE_MCS]),
5848 mask.control[band].mcs))
5849 return -EINVAL;
5850 }
5851
5852 if (mask.control[band].legacy == 0) {
5853 /* don't allow empty legacy rates if HT
5854 * is not even supported. */
5855 if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
5856 return -EINVAL;
5857
5858 for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
5859 if (mask.control[band].mcs[i])
5860 break;
5861
5862 /* legacy and mcs rates may not be both empty */
5863 if (i == IEEE80211_HT_MCS_MASK_LEN)
Johannes Berg4c476992010-10-04 21:36:35 +02005864 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005865 }
5866 }
5867
Johannes Berg4c476992010-10-04 21:36:35 +02005868 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005869}
5870
Johannes Berg2e161f72010-08-12 15:38:38 +02005871static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005872{
Johannes Berg4c476992010-10-04 21:36:35 +02005873 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5874 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02005875 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02005876
5877 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
5878 return -EINVAL;
5879
Johannes Berg2e161f72010-08-12 15:38:38 +02005880 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
5881 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02005882
Johannes Berg9d38d852010-06-09 17:20:33 +02005883 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005884 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005885 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5886 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5887 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005888 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005889 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5890 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005891
5892 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005893 if (!rdev->ops->mgmt_tx)
5894 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005895
Johannes Berg4c476992010-10-04 21:36:35 +02005896 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02005897 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005898 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5899 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005900}
5901
Johannes Berg2e161f72010-08-12 15:38:38 +02005902static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005903{
Johannes Berg4c476992010-10-04 21:36:35 +02005904 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5905 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005906 struct ieee80211_channel *chan;
5907 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005908 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005909 u32 freq;
5910 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01005911 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005912 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01005913 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005914 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01005915 bool offchan, no_cck, dont_wait_for_ack;
5916
5917 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02005918
5919 if (!info->attrs[NL80211_ATTR_FRAME] ||
5920 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5921 return -EINVAL;
5922
Johannes Berg4c476992010-10-04 21:36:35 +02005923 if (!rdev->ops->mgmt_tx)
5924 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005925
Johannes Berg9d38d852010-06-09 17:20:33 +02005926 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005927 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005928 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5929 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5930 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005931 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005932 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5933 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005934
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005935 if (info->attrs[NL80211_ATTR_DURATION]) {
Johannes Berg7c4ef712011-11-18 15:33:48 +01005936 if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005937 return -EINVAL;
5938 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
Johannes Bergebf348f2012-06-01 12:50:54 +02005939
5940 /*
5941 * We should wait on the channel for at least a minimum amount
5942 * of time (10ms) but no longer than the driver supports.
5943 */
5944 if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
5945 wait > rdev->wiphy.max_remain_on_channel_duration)
5946 return -EINVAL;
5947
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005948 }
5949
Jouni Malinen026331c2010-02-15 12:53:10 +02005950 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
Johannes Bergcd6c6592012-05-10 21:27:18 +02005951 if (!nl80211_valid_channel_type(info, &channel_type))
Johannes Berg4c476992010-10-04 21:36:35 +02005952 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005953 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005954 }
5955
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005956 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5957
Johannes Berg7c4ef712011-11-18 15:33:48 +01005958 if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
5959 return -EINVAL;
5960
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305961 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5962
Jouni Malinen026331c2010-02-15 12:53:10 +02005963 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5964 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005965 if (chan == NULL)
5966 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005967
Johannes Berge247bd902011-11-04 11:18:21 +01005968 if (!dont_wait_for_ack) {
5969 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5970 if (!msg)
5971 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005972
Johannes Berge247bd902011-11-04 11:18:21 +01005973 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5974 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005975
Johannes Berge247bd902011-11-04 11:18:21 +01005976 if (IS_ERR(hdr)) {
5977 err = PTR_ERR(hdr);
5978 goto free_msg;
5979 }
Jouni Malinen026331c2010-02-15 12:53:10 +02005980 }
Johannes Berge247bd902011-11-04 11:18:21 +01005981
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005982 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5983 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02005984 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5985 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01005986 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005987 if (err)
5988 goto free_msg;
5989
Johannes Berge247bd902011-11-04 11:18:21 +01005990 if (msg) {
David S. Miller9360ffd2012-03-29 04:41:26 -04005991 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
5992 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02005993
Johannes Berge247bd902011-11-04 11:18:21 +01005994 genlmsg_end(msg, hdr);
5995 return genlmsg_reply(msg, info);
5996 }
5997
5998 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02005999
6000 nla_put_failure:
6001 err = -ENOBUFS;
6002 free_msg:
6003 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02006004 return err;
6005}
6006
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006007static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
6008{
6009 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6010 struct net_device *dev = info->user_ptr[1];
6011 u64 cookie;
6012
6013 if (!info->attrs[NL80211_ATTR_COOKIE])
6014 return -EINVAL;
6015
6016 if (!rdev->ops->mgmt_tx_cancel_wait)
6017 return -EOPNOTSUPP;
6018
6019 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
6020 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
6021 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
6022 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
6023 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
6024 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
6025 return -EOPNOTSUPP;
6026
6027 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
6028
6029 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
6030}
6031
Kalle Valoffb9eb32010-02-17 17:58:10 +02006032static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
6033{
Johannes Berg4c476992010-10-04 21:36:35 +02006034 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006035 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02006036 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006037 u8 ps_state;
6038 bool state;
6039 int err;
6040
Johannes Berg4c476992010-10-04 21:36:35 +02006041 if (!info->attrs[NL80211_ATTR_PS_STATE])
6042 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006043
6044 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
6045
Johannes Berg4c476992010-10-04 21:36:35 +02006046 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
6047 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006048
6049 wdev = dev->ieee80211_ptr;
6050
Johannes Berg4c476992010-10-04 21:36:35 +02006051 if (!rdev->ops->set_power_mgmt)
6052 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006053
6054 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
6055
6056 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02006057 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006058
Johannes Berg4c476992010-10-04 21:36:35 +02006059 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
6060 wdev->ps_timeout);
6061 if (!err)
6062 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006063 return err;
6064}
6065
6066static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
6067{
Johannes Berg4c476992010-10-04 21:36:35 +02006068 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006069 enum nl80211_ps_state ps_state;
6070 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02006071 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02006072 struct sk_buff *msg;
6073 void *hdr;
6074 int err;
6075
Kalle Valoffb9eb32010-02-17 17:58:10 +02006076 wdev = dev->ieee80211_ptr;
6077
Johannes Berg4c476992010-10-04 21:36:35 +02006078 if (!rdev->ops->set_power_mgmt)
6079 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006080
6081 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02006082 if (!msg)
6083 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006084
6085 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6086 NL80211_CMD_GET_POWER_SAVE);
6087 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02006088 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006089 goto free_msg;
6090 }
6091
6092 if (wdev->ps)
6093 ps_state = NL80211_PS_ENABLED;
6094 else
6095 ps_state = NL80211_PS_DISABLED;
6096
David S. Miller9360ffd2012-03-29 04:41:26 -04006097 if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state))
6098 goto nla_put_failure;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006099
6100 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02006101 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02006102
Johannes Berg4c476992010-10-04 21:36:35 +02006103 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02006104 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02006105 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02006106 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02006107 return err;
6108}
6109
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006110static struct nla_policy
6111nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
6112 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
6113 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
6114 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
6115};
6116
6117static int nl80211_set_cqm_rssi(struct genl_info *info,
6118 s32 threshold, u32 hysteresis)
6119{
Johannes Berg4c476992010-10-04 21:36:35 +02006120 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006121 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02006122 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006123
6124 if (threshold > 0)
6125 return -EINVAL;
6126
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006127 wdev = dev->ieee80211_ptr;
6128
Johannes Berg4c476992010-10-04 21:36:35 +02006129 if (!rdev->ops->set_cqm_rssi_config)
6130 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006131
Johannes Berg074ac8d2010-09-16 14:58:22 +02006132 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006133 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
6134 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006135
Johannes Berg4c476992010-10-04 21:36:35 +02006136 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
6137 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006138}
6139
6140static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
6141{
6142 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
6143 struct nlattr *cqm;
6144 int err;
6145
6146 cqm = info->attrs[NL80211_ATTR_CQM];
6147 if (!cqm) {
6148 err = -EINVAL;
6149 goto out;
6150 }
6151
6152 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
6153 nl80211_attr_cqm_policy);
6154 if (err)
6155 goto out;
6156
6157 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
6158 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
6159 s32 threshold;
6160 u32 hysteresis;
6161 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
6162 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
6163 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
6164 } else
6165 err = -EINVAL;
6166
6167out:
6168 return err;
6169}
6170
Johannes Berg29cbe682010-12-03 09:20:44 +01006171static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
6172{
6173 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6174 struct net_device *dev = info->user_ptr[1];
6175 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08006176 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01006177 int err;
6178
6179 /* start with default */
6180 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08006181 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01006182
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006183 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01006184 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006185 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01006186 if (err)
6187 return err;
6188 }
6189
6190 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
6191 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
6192 return -EINVAL;
6193
Javier Cardonac80d5452010-12-16 17:37:49 -08006194 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
6195 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
6196
Chun-Yeow Yeoh4bb62342011-11-24 17:15:20 -08006197 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
6198 !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
6199 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
6200 return -EINVAL;
6201
Javier Cardonac80d5452010-12-16 17:37:49 -08006202 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
6203 /* parse additional setup parameters if given */
6204 err = nl80211_parse_mesh_setup(info, &setup);
6205 if (err)
6206 return err;
6207 }
6208
Johannes Bergcc1d2802012-05-16 23:50:20 +02006209 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
6210 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
6211
6212 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
6213 !nl80211_valid_channel_type(info, &channel_type))
6214 return -EINVAL;
6215
6216 setup.channel = rdev_freq_to_chan(rdev,
6217 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
6218 channel_type);
6219 if (!setup.channel)
6220 return -EINVAL;
6221 setup.channel_type = channel_type;
6222 } else {
6223 /* cfg80211_join_mesh() will sort it out */
6224 setup.channel = NULL;
6225 }
6226
Javier Cardonac80d5452010-12-16 17:37:49 -08006227 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01006228}
6229
6230static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
6231{
6232 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6233 struct net_device *dev = info->user_ptr[1];
6234
6235 return cfg80211_leave_mesh(rdev, dev);
6236}
6237
Johannes Bergff1b6e62011-05-04 15:37:28 +02006238static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
6239{
6240 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6241 struct sk_buff *msg;
6242 void *hdr;
6243
6244 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6245 return -EOPNOTSUPP;
6246
6247 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6248 if (!msg)
6249 return -ENOMEM;
6250
6251 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6252 NL80211_CMD_GET_WOWLAN);
6253 if (!hdr)
6254 goto nla_put_failure;
6255
6256 if (rdev->wowlan) {
6257 struct nlattr *nl_wowlan;
6258
6259 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
6260 if (!nl_wowlan)
6261 goto nla_put_failure;
6262
David S. Miller9360ffd2012-03-29 04:41:26 -04006263 if ((rdev->wowlan->any &&
6264 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
6265 (rdev->wowlan->disconnect &&
6266 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
6267 (rdev->wowlan->magic_pkt &&
6268 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
6269 (rdev->wowlan->gtk_rekey_failure &&
6270 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
6271 (rdev->wowlan->eap_identity_req &&
6272 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
6273 (rdev->wowlan->four_way_handshake &&
6274 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
6275 (rdev->wowlan->rfkill_release &&
6276 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
6277 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02006278 if (rdev->wowlan->n_patterns) {
6279 struct nlattr *nl_pats, *nl_pat;
6280 int i, pat_len;
6281
6282 nl_pats = nla_nest_start(msg,
6283 NL80211_WOWLAN_TRIG_PKT_PATTERN);
6284 if (!nl_pats)
6285 goto nla_put_failure;
6286
6287 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
6288 nl_pat = nla_nest_start(msg, i + 1);
6289 if (!nl_pat)
6290 goto nla_put_failure;
6291 pat_len = rdev->wowlan->patterns[i].pattern_len;
David S. Miller9360ffd2012-03-29 04:41:26 -04006292 if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
6293 DIV_ROUND_UP(pat_len, 8),
6294 rdev->wowlan->patterns[i].mask) ||
6295 nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
6296 pat_len,
6297 rdev->wowlan->patterns[i].pattern))
6298 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02006299 nla_nest_end(msg, nl_pat);
6300 }
6301 nla_nest_end(msg, nl_pats);
6302 }
6303
6304 nla_nest_end(msg, nl_wowlan);
6305 }
6306
6307 genlmsg_end(msg, hdr);
6308 return genlmsg_reply(msg, info);
6309
6310nla_put_failure:
6311 nlmsg_free(msg);
6312 return -ENOBUFS;
6313}
6314
6315static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
6316{
6317 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6318 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
6319 struct cfg80211_wowlan no_triggers = {};
6320 struct cfg80211_wowlan new_triggers = {};
6321 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
6322 int err, i;
Johannes Berg6d525632012-04-04 15:05:25 +02006323 bool prev_enabled = rdev->wowlan;
Johannes Bergff1b6e62011-05-04 15:37:28 +02006324
6325 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6326 return -EOPNOTSUPP;
6327
6328 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
6329 goto no_triggers;
6330
6331 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
6332 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6333 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6334 nl80211_wowlan_policy);
6335 if (err)
6336 return err;
6337
6338 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
6339 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
6340 return -EINVAL;
6341 new_triggers.any = true;
6342 }
6343
6344 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
6345 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
6346 return -EINVAL;
6347 new_triggers.disconnect = true;
6348 }
6349
6350 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
6351 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
6352 return -EINVAL;
6353 new_triggers.magic_pkt = true;
6354 }
6355
Johannes Berg77dbbb12011-07-13 10:48:55 +02006356 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
6357 return -EINVAL;
6358
6359 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
6360 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
6361 return -EINVAL;
6362 new_triggers.gtk_rekey_failure = true;
6363 }
6364
6365 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
6366 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
6367 return -EINVAL;
6368 new_triggers.eap_identity_req = true;
6369 }
6370
6371 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
6372 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
6373 return -EINVAL;
6374 new_triggers.four_way_handshake = true;
6375 }
6376
6377 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
6378 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
6379 return -EINVAL;
6380 new_triggers.rfkill_release = true;
6381 }
6382
Johannes Bergff1b6e62011-05-04 15:37:28 +02006383 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
6384 struct nlattr *pat;
6385 int n_patterns = 0;
6386 int rem, pat_len, mask_len;
6387 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
6388
6389 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6390 rem)
6391 n_patterns++;
6392 if (n_patterns > wowlan->n_patterns)
6393 return -EINVAL;
6394
6395 new_triggers.patterns = kcalloc(n_patterns,
6396 sizeof(new_triggers.patterns[0]),
6397 GFP_KERNEL);
6398 if (!new_triggers.patterns)
6399 return -ENOMEM;
6400
6401 new_triggers.n_patterns = n_patterns;
6402 i = 0;
6403
6404 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6405 rem) {
6406 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
6407 nla_data(pat), nla_len(pat), NULL);
6408 err = -EINVAL;
6409 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
6410 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
6411 goto error;
6412 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
6413 mask_len = DIV_ROUND_UP(pat_len, 8);
6414 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
6415 mask_len)
6416 goto error;
6417 if (pat_len > wowlan->pattern_max_len ||
6418 pat_len < wowlan->pattern_min_len)
6419 goto error;
6420
6421 new_triggers.patterns[i].mask =
6422 kmalloc(mask_len + pat_len, GFP_KERNEL);
6423 if (!new_triggers.patterns[i].mask) {
6424 err = -ENOMEM;
6425 goto error;
6426 }
6427 new_triggers.patterns[i].pattern =
6428 new_triggers.patterns[i].mask + mask_len;
6429 memcpy(new_triggers.patterns[i].mask,
6430 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
6431 mask_len);
6432 new_triggers.patterns[i].pattern_len = pat_len;
6433 memcpy(new_triggers.patterns[i].pattern,
6434 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
6435 pat_len);
6436 i++;
6437 }
6438 }
6439
6440 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
6441 struct cfg80211_wowlan *ntrig;
6442 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
6443 GFP_KERNEL);
6444 if (!ntrig) {
6445 err = -ENOMEM;
6446 goto error;
6447 }
6448 cfg80211_rdev_free_wowlan(rdev);
6449 rdev->wowlan = ntrig;
6450 } else {
6451 no_triggers:
6452 cfg80211_rdev_free_wowlan(rdev);
6453 rdev->wowlan = NULL;
6454 }
6455
Johannes Berg6d525632012-04-04 15:05:25 +02006456 if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
6457 rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
6458
Johannes Bergff1b6e62011-05-04 15:37:28 +02006459 return 0;
6460 error:
6461 for (i = 0; i < new_triggers.n_patterns; i++)
6462 kfree(new_triggers.patterns[i].mask);
6463 kfree(new_triggers.patterns);
6464 return err;
6465}
6466
Johannes Berge5497d72011-07-05 16:35:40 +02006467static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
6468{
6469 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6470 struct net_device *dev = info->user_ptr[1];
6471 struct wireless_dev *wdev = dev->ieee80211_ptr;
6472 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
6473 struct cfg80211_gtk_rekey_data rekey_data;
6474 int err;
6475
6476 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
6477 return -EINVAL;
6478
6479 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
6480 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
6481 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
6482 nl80211_rekey_policy);
6483 if (err)
6484 return err;
6485
6486 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
6487 return -ERANGE;
6488 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
6489 return -ERANGE;
6490 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
6491 return -ERANGE;
6492
6493 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
6494 NL80211_KEK_LEN);
6495 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
6496 NL80211_KCK_LEN);
6497 memcpy(rekey_data.replay_ctr,
6498 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
6499 NL80211_REPLAY_CTR_LEN);
6500
6501 wdev_lock(wdev);
6502 if (!wdev->current_bss) {
6503 err = -ENOTCONN;
6504 goto out;
6505 }
6506
6507 if (!rdev->ops->set_rekey_data) {
6508 err = -EOPNOTSUPP;
6509 goto out;
6510 }
6511
6512 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
6513 out:
6514 wdev_unlock(wdev);
6515 return err;
6516}
6517
Johannes Berg28946da2011-11-04 11:18:12 +01006518static int nl80211_register_unexpected_frame(struct sk_buff *skb,
6519 struct genl_info *info)
6520{
6521 struct net_device *dev = info->user_ptr[1];
6522 struct wireless_dev *wdev = dev->ieee80211_ptr;
6523
6524 if (wdev->iftype != NL80211_IFTYPE_AP &&
6525 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6526 return -EINVAL;
6527
6528 if (wdev->ap_unexpected_nlpid)
6529 return -EBUSY;
6530
6531 wdev->ap_unexpected_nlpid = info->snd_pid;
6532 return 0;
6533}
6534
Johannes Berg7f6cf312011-11-04 11:18:15 +01006535static int nl80211_probe_client(struct sk_buff *skb,
6536 struct genl_info *info)
6537{
6538 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6539 struct net_device *dev = info->user_ptr[1];
6540 struct wireless_dev *wdev = dev->ieee80211_ptr;
6541 struct sk_buff *msg;
6542 void *hdr;
6543 const u8 *addr;
6544 u64 cookie;
6545 int err;
6546
6547 if (wdev->iftype != NL80211_IFTYPE_AP &&
6548 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6549 return -EOPNOTSUPP;
6550
6551 if (!info->attrs[NL80211_ATTR_MAC])
6552 return -EINVAL;
6553
6554 if (!rdev->ops->probe_client)
6555 return -EOPNOTSUPP;
6556
6557 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6558 if (!msg)
6559 return -ENOMEM;
6560
6561 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6562 NL80211_CMD_PROBE_CLIENT);
6563
6564 if (IS_ERR(hdr)) {
6565 err = PTR_ERR(hdr);
6566 goto free_msg;
6567 }
6568
6569 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
6570
6571 err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
6572 if (err)
6573 goto free_msg;
6574
David S. Miller9360ffd2012-03-29 04:41:26 -04006575 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
6576 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01006577
6578 genlmsg_end(msg, hdr);
6579
6580 return genlmsg_reply(msg, info);
6581
6582 nla_put_failure:
6583 err = -ENOBUFS;
6584 free_msg:
6585 nlmsg_free(msg);
6586 return err;
6587}
6588
Johannes Berg5e7602302011-11-04 11:18:17 +01006589static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
6590{
6591 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6592
6593 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
6594 return -EOPNOTSUPP;
6595
6596 if (rdev->ap_beacons_nlpid)
6597 return -EBUSY;
6598
6599 rdev->ap_beacons_nlpid = info->snd_pid;
6600
6601 return 0;
6602}
6603
Johannes Berg4c476992010-10-04 21:36:35 +02006604#define NL80211_FLAG_NEED_WIPHY 0x01
6605#define NL80211_FLAG_NEED_NETDEV 0x02
6606#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02006607#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
6608#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
6609 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02006610
6611static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
6612 struct genl_info *info)
6613{
6614 struct cfg80211_registered_device *rdev;
6615 struct net_device *dev;
6616 int err;
6617 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
6618
6619 if (rtnl)
6620 rtnl_lock();
6621
6622 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
6623 rdev = cfg80211_get_dev_from_info(info);
6624 if (IS_ERR(rdev)) {
6625 if (rtnl)
6626 rtnl_unlock();
6627 return PTR_ERR(rdev);
6628 }
6629 info->user_ptr[0] = rdev;
6630 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
Johannes Berg00918d32011-12-13 17:22:05 +01006631 err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs,
6632 &rdev, &dev);
Johannes Berg4c476992010-10-04 21:36:35 +02006633 if (err) {
6634 if (rtnl)
6635 rtnl_unlock();
6636 return err;
6637 }
Johannes Berg41265712010-10-04 21:14:05 +02006638 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
6639 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02006640 cfg80211_unlock_rdev(rdev);
6641 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02006642 if (rtnl)
6643 rtnl_unlock();
6644 return -ENETDOWN;
6645 }
Johannes Berg4c476992010-10-04 21:36:35 +02006646 info->user_ptr[0] = rdev;
6647 info->user_ptr[1] = dev;
6648 }
6649
6650 return 0;
6651}
6652
6653static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
6654 struct genl_info *info)
6655{
6656 if (info->user_ptr[0])
6657 cfg80211_unlock_rdev(info->user_ptr[0]);
6658 if (info->user_ptr[1])
6659 dev_put(info->user_ptr[1]);
6660 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
6661 rtnl_unlock();
6662}
6663
Johannes Berg55682962007-09-20 13:09:35 -04006664static struct genl_ops nl80211_ops[] = {
6665 {
6666 .cmd = NL80211_CMD_GET_WIPHY,
6667 .doit = nl80211_get_wiphy,
6668 .dumpit = nl80211_dump_wiphy,
6669 .policy = nl80211_policy,
6670 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006671 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04006672 },
6673 {
6674 .cmd = NL80211_CMD_SET_WIPHY,
6675 .doit = nl80211_set_wiphy,
6676 .policy = nl80211_policy,
6677 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006678 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006679 },
6680 {
6681 .cmd = NL80211_CMD_GET_INTERFACE,
6682 .doit = nl80211_get_interface,
6683 .dumpit = nl80211_dump_interface,
6684 .policy = nl80211_policy,
6685 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006686 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04006687 },
6688 {
6689 .cmd = NL80211_CMD_SET_INTERFACE,
6690 .doit = nl80211_set_interface,
6691 .policy = nl80211_policy,
6692 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006693 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6694 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006695 },
6696 {
6697 .cmd = NL80211_CMD_NEW_INTERFACE,
6698 .doit = nl80211_new_interface,
6699 .policy = nl80211_policy,
6700 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006701 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6702 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006703 },
6704 {
6705 .cmd = NL80211_CMD_DEL_INTERFACE,
6706 .doit = nl80211_del_interface,
6707 .policy = nl80211_policy,
6708 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006709 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6710 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006711 },
Johannes Berg41ade002007-12-19 02:03:29 +01006712 {
6713 .cmd = NL80211_CMD_GET_KEY,
6714 .doit = nl80211_get_key,
6715 .policy = nl80211_policy,
6716 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006717 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006718 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006719 },
6720 {
6721 .cmd = NL80211_CMD_SET_KEY,
6722 .doit = nl80211_set_key,
6723 .policy = nl80211_policy,
6724 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006725 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006726 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006727 },
6728 {
6729 .cmd = NL80211_CMD_NEW_KEY,
6730 .doit = nl80211_new_key,
6731 .policy = nl80211_policy,
6732 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006733 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006734 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006735 },
6736 {
6737 .cmd = NL80211_CMD_DEL_KEY,
6738 .doit = nl80211_del_key,
6739 .policy = nl80211_policy,
6740 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006741 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006742 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006743 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01006744 {
6745 .cmd = NL80211_CMD_SET_BEACON,
6746 .policy = nl80211_policy,
6747 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006748 .doit = nl80211_set_beacon,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006749 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006750 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006751 },
6752 {
Johannes Berg88600202012-02-13 15:17:18 +01006753 .cmd = NL80211_CMD_START_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006754 .policy = nl80211_policy,
6755 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006756 .doit = nl80211_start_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006757 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006758 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006759 },
6760 {
Johannes Berg88600202012-02-13 15:17:18 +01006761 .cmd = NL80211_CMD_STOP_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006762 .policy = nl80211_policy,
6763 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006764 .doit = nl80211_stop_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006765 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006766 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006767 },
Johannes Berg5727ef12007-12-19 02:03:34 +01006768 {
6769 .cmd = NL80211_CMD_GET_STATION,
6770 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006771 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01006772 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02006773 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6774 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006775 },
6776 {
6777 .cmd = NL80211_CMD_SET_STATION,
6778 .doit = nl80211_set_station,
6779 .policy = nl80211_policy,
6780 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006781 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006782 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006783 },
6784 {
6785 .cmd = NL80211_CMD_NEW_STATION,
6786 .doit = nl80211_new_station,
6787 .policy = nl80211_policy,
6788 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006789 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006790 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006791 },
6792 {
6793 .cmd = NL80211_CMD_DEL_STATION,
6794 .doit = nl80211_del_station,
6795 .policy = nl80211_policy,
6796 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006797 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006798 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006799 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006800 {
6801 .cmd = NL80211_CMD_GET_MPATH,
6802 .doit = nl80211_get_mpath,
6803 .dumpit = nl80211_dump_mpath,
6804 .policy = nl80211_policy,
6805 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006806 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006807 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006808 },
6809 {
6810 .cmd = NL80211_CMD_SET_MPATH,
6811 .doit = nl80211_set_mpath,
6812 .policy = nl80211_policy,
6813 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006814 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006815 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006816 },
6817 {
6818 .cmd = NL80211_CMD_NEW_MPATH,
6819 .doit = nl80211_new_mpath,
6820 .policy = nl80211_policy,
6821 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006822 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006823 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006824 },
6825 {
6826 .cmd = NL80211_CMD_DEL_MPATH,
6827 .doit = nl80211_del_mpath,
6828 .policy = nl80211_policy,
6829 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006830 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006831 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006832 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006833 {
6834 .cmd = NL80211_CMD_SET_BSS,
6835 .doit = nl80211_set_bss,
6836 .policy = nl80211_policy,
6837 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006838 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006839 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006840 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006841 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08006842 .cmd = NL80211_CMD_GET_REG,
6843 .doit = nl80211_get_reg,
6844 .policy = nl80211_policy,
6845 /* can be retrieved by unprivileged users */
6846 },
6847 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006848 .cmd = NL80211_CMD_SET_REG,
6849 .doit = nl80211_set_reg,
6850 .policy = nl80211_policy,
6851 .flags = GENL_ADMIN_PERM,
6852 },
6853 {
6854 .cmd = NL80211_CMD_REQ_SET_REG,
6855 .doit = nl80211_req_set_reg,
6856 .policy = nl80211_policy,
6857 .flags = GENL_ADMIN_PERM,
6858 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006859 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006860 .cmd = NL80211_CMD_GET_MESH_CONFIG,
6861 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006862 .policy = nl80211_policy,
6863 /* can be retrieved by unprivileged users */
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006864 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006865 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006866 },
6867 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006868 .cmd = NL80211_CMD_SET_MESH_CONFIG,
6869 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006870 .policy = nl80211_policy,
6871 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01006872 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006873 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006874 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02006875 {
Johannes Berg2a519312009-02-10 21:25:55 +01006876 .cmd = NL80211_CMD_TRIGGER_SCAN,
6877 .doit = nl80211_trigger_scan,
6878 .policy = nl80211_policy,
6879 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006880 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006881 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01006882 },
6883 {
6884 .cmd = NL80211_CMD_GET_SCAN,
6885 .policy = nl80211_policy,
6886 .dumpit = nl80211_dump_scan,
6887 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02006888 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03006889 .cmd = NL80211_CMD_START_SCHED_SCAN,
6890 .doit = nl80211_start_sched_scan,
6891 .policy = nl80211_policy,
6892 .flags = GENL_ADMIN_PERM,
6893 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6894 NL80211_FLAG_NEED_RTNL,
6895 },
6896 {
6897 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
6898 .doit = nl80211_stop_sched_scan,
6899 .policy = nl80211_policy,
6900 .flags = GENL_ADMIN_PERM,
6901 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6902 NL80211_FLAG_NEED_RTNL,
6903 },
6904 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02006905 .cmd = NL80211_CMD_AUTHENTICATE,
6906 .doit = nl80211_authenticate,
6907 .policy = nl80211_policy,
6908 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006909 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006910 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006911 },
6912 {
6913 .cmd = NL80211_CMD_ASSOCIATE,
6914 .doit = nl80211_associate,
6915 .policy = nl80211_policy,
6916 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006917 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006918 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006919 },
6920 {
6921 .cmd = NL80211_CMD_DEAUTHENTICATE,
6922 .doit = nl80211_deauthenticate,
6923 .policy = nl80211_policy,
6924 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006925 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006926 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006927 },
6928 {
6929 .cmd = NL80211_CMD_DISASSOCIATE,
6930 .doit = nl80211_disassociate,
6931 .policy = nl80211_policy,
6932 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006933 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006934 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006935 },
Johannes Berg04a773a2009-04-19 21:24:32 +02006936 {
6937 .cmd = NL80211_CMD_JOIN_IBSS,
6938 .doit = nl80211_join_ibss,
6939 .policy = nl80211_policy,
6940 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006941 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006942 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006943 },
6944 {
6945 .cmd = NL80211_CMD_LEAVE_IBSS,
6946 .doit = nl80211_leave_ibss,
6947 .policy = nl80211_policy,
6948 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006949 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006950 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006951 },
Johannes Bergaff89a92009-07-01 21:26:51 +02006952#ifdef CONFIG_NL80211_TESTMODE
6953 {
6954 .cmd = NL80211_CMD_TESTMODE,
6955 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006956 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02006957 .policy = nl80211_policy,
6958 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006959 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6960 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02006961 },
6962#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02006963 {
6964 .cmd = NL80211_CMD_CONNECT,
6965 .doit = nl80211_connect,
6966 .policy = nl80211_policy,
6967 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006968 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006969 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006970 },
6971 {
6972 .cmd = NL80211_CMD_DISCONNECT,
6973 .doit = nl80211_disconnect,
6974 .policy = nl80211_policy,
6975 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006976 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006977 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006978 },
Johannes Berg463d0182009-07-14 00:33:35 +02006979 {
6980 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
6981 .doit = nl80211_wiphy_netns,
6982 .policy = nl80211_policy,
6983 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006984 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6985 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02006986 },
Holger Schurig61fa7132009-11-11 12:25:40 +01006987 {
6988 .cmd = NL80211_CMD_GET_SURVEY,
6989 .policy = nl80211_policy,
6990 .dumpit = nl80211_dump_survey,
6991 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006992 {
6993 .cmd = NL80211_CMD_SET_PMKSA,
6994 .doit = nl80211_setdel_pmksa,
6995 .policy = nl80211_policy,
6996 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006997 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006998 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006999 },
7000 {
7001 .cmd = NL80211_CMD_DEL_PMKSA,
7002 .doit = nl80211_setdel_pmksa,
7003 .policy = nl80211_policy,
7004 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02007005 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007006 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01007007 },
7008 {
7009 .cmd = NL80211_CMD_FLUSH_PMKSA,
7010 .doit = nl80211_flush_pmksa,
7011 .policy = nl80211_policy,
7012 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02007013 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007014 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01007015 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007016 {
7017 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
7018 .doit = nl80211_remain_on_channel,
7019 .policy = nl80211_policy,
7020 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02007021 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007022 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007023 },
7024 {
7025 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7026 .doit = nl80211_cancel_remain_on_channel,
7027 .policy = nl80211_policy,
7028 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02007029 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007030 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007031 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007032 {
7033 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
7034 .doit = nl80211_set_tx_bitrate_mask,
7035 .policy = nl80211_policy,
7036 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007037 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7038 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02007039 },
Jouni Malinen026331c2010-02-15 12:53:10 +02007040 {
Johannes Berg2e161f72010-08-12 15:38:38 +02007041 .cmd = NL80211_CMD_REGISTER_FRAME,
7042 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02007043 .policy = nl80211_policy,
7044 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007045 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7046 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02007047 },
7048 {
Johannes Berg2e161f72010-08-12 15:38:38 +02007049 .cmd = NL80211_CMD_FRAME,
7050 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02007051 .policy = nl80211_policy,
7052 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02007053 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02007054 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02007055 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02007056 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01007057 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
7058 .doit = nl80211_tx_mgmt_cancel_wait,
7059 .policy = nl80211_policy,
7060 .flags = GENL_ADMIN_PERM,
7061 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7062 NL80211_FLAG_NEED_RTNL,
7063 },
7064 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02007065 .cmd = NL80211_CMD_SET_POWER_SAVE,
7066 .doit = nl80211_set_power_save,
7067 .policy = nl80211_policy,
7068 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007069 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7070 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02007071 },
7072 {
7073 .cmd = NL80211_CMD_GET_POWER_SAVE,
7074 .doit = nl80211_get_power_save,
7075 .policy = nl80211_policy,
7076 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02007077 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7078 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02007079 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007080 {
7081 .cmd = NL80211_CMD_SET_CQM,
7082 .doit = nl80211_set_cqm,
7083 .policy = nl80211_policy,
7084 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007085 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7086 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007087 },
Johannes Bergf444de02010-05-05 15:25:02 +02007088 {
7089 .cmd = NL80211_CMD_SET_CHANNEL,
7090 .doit = nl80211_set_channel,
7091 .policy = nl80211_policy,
7092 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007093 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7094 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02007095 },
Bill Jordane8347eb2010-10-01 13:54:28 -04007096 {
7097 .cmd = NL80211_CMD_SET_WDS_PEER,
7098 .doit = nl80211_set_wds_peer,
7099 .policy = nl80211_policy,
7100 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02007101 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7102 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04007103 },
Johannes Berg29cbe682010-12-03 09:20:44 +01007104 {
7105 .cmd = NL80211_CMD_JOIN_MESH,
7106 .doit = nl80211_join_mesh,
7107 .policy = nl80211_policy,
7108 .flags = GENL_ADMIN_PERM,
7109 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7110 NL80211_FLAG_NEED_RTNL,
7111 },
7112 {
7113 .cmd = NL80211_CMD_LEAVE_MESH,
7114 .doit = nl80211_leave_mesh,
7115 .policy = nl80211_policy,
7116 .flags = GENL_ADMIN_PERM,
7117 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7118 NL80211_FLAG_NEED_RTNL,
7119 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02007120 {
7121 .cmd = NL80211_CMD_GET_WOWLAN,
7122 .doit = nl80211_get_wowlan,
7123 .policy = nl80211_policy,
7124 /* can be retrieved by unprivileged users */
7125 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7126 NL80211_FLAG_NEED_RTNL,
7127 },
7128 {
7129 .cmd = NL80211_CMD_SET_WOWLAN,
7130 .doit = nl80211_set_wowlan,
7131 .policy = nl80211_policy,
7132 .flags = GENL_ADMIN_PERM,
7133 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7134 NL80211_FLAG_NEED_RTNL,
7135 },
Johannes Berge5497d72011-07-05 16:35:40 +02007136 {
7137 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
7138 .doit = nl80211_set_rekey_data,
7139 .policy = nl80211_policy,
7140 .flags = GENL_ADMIN_PERM,
7141 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7142 NL80211_FLAG_NEED_RTNL,
7143 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03007144 {
7145 .cmd = NL80211_CMD_TDLS_MGMT,
7146 .doit = nl80211_tdls_mgmt,
7147 .policy = nl80211_policy,
7148 .flags = GENL_ADMIN_PERM,
7149 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7150 NL80211_FLAG_NEED_RTNL,
7151 },
7152 {
7153 .cmd = NL80211_CMD_TDLS_OPER,
7154 .doit = nl80211_tdls_oper,
7155 .policy = nl80211_policy,
7156 .flags = GENL_ADMIN_PERM,
7157 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7158 NL80211_FLAG_NEED_RTNL,
7159 },
Johannes Berg28946da2011-11-04 11:18:12 +01007160 {
7161 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
7162 .doit = nl80211_register_unexpected_frame,
7163 .policy = nl80211_policy,
7164 .flags = GENL_ADMIN_PERM,
7165 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7166 NL80211_FLAG_NEED_RTNL,
7167 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01007168 {
7169 .cmd = NL80211_CMD_PROBE_CLIENT,
7170 .doit = nl80211_probe_client,
7171 .policy = nl80211_policy,
7172 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02007173 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg7f6cf312011-11-04 11:18:15 +01007174 NL80211_FLAG_NEED_RTNL,
7175 },
Johannes Berg5e7602302011-11-04 11:18:17 +01007176 {
7177 .cmd = NL80211_CMD_REGISTER_BEACONS,
7178 .doit = nl80211_register_beacons,
7179 .policy = nl80211_policy,
7180 .flags = GENL_ADMIN_PERM,
7181 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7182 NL80211_FLAG_NEED_RTNL,
7183 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01007184 {
7185 .cmd = NL80211_CMD_SET_NOACK_MAP,
7186 .doit = nl80211_set_noack_map,
7187 .policy = nl80211_policy,
7188 .flags = GENL_ADMIN_PERM,
7189 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7190 NL80211_FLAG_NEED_RTNL,
7191 },
7192
Johannes Berg55682962007-09-20 13:09:35 -04007193};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007194
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007195static struct genl_multicast_group nl80211_mlme_mcgrp = {
7196 .name = "mlme",
7197};
Johannes Berg55682962007-09-20 13:09:35 -04007198
7199/* multicast groups */
7200static struct genl_multicast_group nl80211_config_mcgrp = {
7201 .name = "config",
7202};
Johannes Berg2a519312009-02-10 21:25:55 +01007203static struct genl_multicast_group nl80211_scan_mcgrp = {
7204 .name = "scan",
7205};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007206static struct genl_multicast_group nl80211_regulatory_mcgrp = {
7207 .name = "regulatory",
7208};
Johannes Berg55682962007-09-20 13:09:35 -04007209
7210/* notification functions */
7211
7212void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
7213{
7214 struct sk_buff *msg;
7215
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007216 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04007217 if (!msg)
7218 return;
7219
7220 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
7221 nlmsg_free(msg);
7222 return;
7223 }
7224
Johannes Berg463d0182009-07-14 00:33:35 +02007225 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7226 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04007227}
7228
Johannes Berg362a4152009-05-24 16:43:15 +02007229static int nl80211_add_scan_req(struct sk_buff *msg,
7230 struct cfg80211_registered_device *rdev)
7231{
7232 struct cfg80211_scan_request *req = rdev->scan_req;
7233 struct nlattr *nest;
7234 int i;
7235
Johannes Berg667503dd2009-07-07 03:56:11 +02007236 ASSERT_RDEV_LOCK(rdev);
7237
Johannes Berg362a4152009-05-24 16:43:15 +02007238 if (WARN_ON(!req))
7239 return 0;
7240
7241 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
7242 if (!nest)
7243 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04007244 for (i = 0; i < req->n_ssids; i++) {
7245 if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid))
7246 goto nla_put_failure;
7247 }
Johannes Berg362a4152009-05-24 16:43:15 +02007248 nla_nest_end(msg, nest);
7249
7250 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
7251 if (!nest)
7252 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04007253 for (i = 0; i < req->n_channels; i++) {
7254 if (nla_put_u32(msg, i, req->channels[i]->center_freq))
7255 goto nla_put_failure;
7256 }
Johannes Berg362a4152009-05-24 16:43:15 +02007257 nla_nest_end(msg, nest);
7258
David S. Miller9360ffd2012-03-29 04:41:26 -04007259 if (req->ie &&
7260 nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
7261 goto nla_put_failure;
Johannes Berg362a4152009-05-24 16:43:15 +02007262
7263 return 0;
7264 nla_put_failure:
7265 return -ENOBUFS;
7266}
7267
Johannes Berga538e2d2009-06-16 19:56:42 +02007268static int nl80211_send_scan_msg(struct sk_buff *msg,
7269 struct cfg80211_registered_device *rdev,
7270 struct net_device *netdev,
7271 u32 pid, u32 seq, int flags,
7272 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01007273{
7274 void *hdr;
7275
7276 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7277 if (!hdr)
7278 return -1;
7279
David S. Miller9360ffd2012-03-29 04:41:26 -04007280 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7281 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
7282 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01007283
Johannes Berg362a4152009-05-24 16:43:15 +02007284 /* ignore errors and send incomplete event anyway */
7285 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01007286
7287 return genlmsg_end(msg, hdr);
7288
7289 nla_put_failure:
7290 genlmsg_cancel(msg, hdr);
7291 return -EMSGSIZE;
7292}
7293
Luciano Coelho807f8a82011-05-11 17:09:35 +03007294static int
7295nl80211_send_sched_scan_msg(struct sk_buff *msg,
7296 struct cfg80211_registered_device *rdev,
7297 struct net_device *netdev,
7298 u32 pid, u32 seq, int flags, u32 cmd)
7299{
7300 void *hdr;
7301
7302 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7303 if (!hdr)
7304 return -1;
7305
David S. Miller9360ffd2012-03-29 04:41:26 -04007306 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7307 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
7308 goto nla_put_failure;
Luciano Coelho807f8a82011-05-11 17:09:35 +03007309
7310 return genlmsg_end(msg, hdr);
7311
7312 nla_put_failure:
7313 genlmsg_cancel(msg, hdr);
7314 return -EMSGSIZE;
7315}
7316
Johannes Berga538e2d2009-06-16 19:56:42 +02007317void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
7318 struct net_device *netdev)
7319{
7320 struct sk_buff *msg;
7321
7322 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7323 if (!msg)
7324 return;
7325
7326 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7327 NL80211_CMD_TRIGGER_SCAN) < 0) {
7328 nlmsg_free(msg);
7329 return;
7330 }
7331
Johannes Berg463d0182009-07-14 00:33:35 +02007332 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7333 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02007334}
7335
Johannes Berg2a519312009-02-10 21:25:55 +01007336void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
7337 struct net_device *netdev)
7338{
7339 struct sk_buff *msg;
7340
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007341 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007342 if (!msg)
7343 return;
7344
Johannes Berga538e2d2009-06-16 19:56:42 +02007345 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7346 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007347 nlmsg_free(msg);
7348 return;
7349 }
7350
Johannes Berg463d0182009-07-14 00:33:35 +02007351 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7352 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007353}
7354
7355void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
7356 struct net_device *netdev)
7357{
7358 struct sk_buff *msg;
7359
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007360 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007361 if (!msg)
7362 return;
7363
Johannes Berga538e2d2009-06-16 19:56:42 +02007364 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7365 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007366 nlmsg_free(msg);
7367 return;
7368 }
7369
Johannes Berg463d0182009-07-14 00:33:35 +02007370 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7371 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007372}
7373
Luciano Coelho807f8a82011-05-11 17:09:35 +03007374void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
7375 struct net_device *netdev)
7376{
7377 struct sk_buff *msg;
7378
7379 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7380 if (!msg)
7381 return;
7382
7383 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
7384 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
7385 nlmsg_free(msg);
7386 return;
7387 }
7388
7389 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7390 nl80211_scan_mcgrp.id, GFP_KERNEL);
7391}
7392
7393void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
7394 struct net_device *netdev, u32 cmd)
7395{
7396 struct sk_buff *msg;
7397
7398 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7399 if (!msg)
7400 return;
7401
7402 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
7403 nlmsg_free(msg);
7404 return;
7405 }
7406
7407 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7408 nl80211_scan_mcgrp.id, GFP_KERNEL);
7409}
7410
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007411/*
7412 * This can happen on global regulatory changes or device specific settings
7413 * based on custom world regulatory domains.
7414 */
7415void nl80211_send_reg_change_event(struct regulatory_request *request)
7416{
7417 struct sk_buff *msg;
7418 void *hdr;
7419
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007420 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007421 if (!msg)
7422 return;
7423
7424 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
7425 if (!hdr) {
7426 nlmsg_free(msg);
7427 return;
7428 }
7429
7430 /* Userspace can always count this one always being set */
David S. Miller9360ffd2012-03-29 04:41:26 -04007431 if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
7432 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007433
David S. Miller9360ffd2012-03-29 04:41:26 -04007434 if (request->alpha2[0] == '0' && request->alpha2[1] == '0') {
7435 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7436 NL80211_REGDOM_TYPE_WORLD))
7437 goto nla_put_failure;
7438 } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') {
7439 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7440 NL80211_REGDOM_TYPE_CUSTOM_WORLD))
7441 goto nla_put_failure;
7442 } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
7443 request->intersect) {
7444 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7445 NL80211_REGDOM_TYPE_INTERSECTION))
7446 goto nla_put_failure;
7447 } else {
7448 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7449 NL80211_REGDOM_TYPE_COUNTRY) ||
7450 nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
7451 request->alpha2))
7452 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007453 }
7454
David S. Miller9360ffd2012-03-29 04:41:26 -04007455 if (wiphy_idx_valid(request->wiphy_idx) &&
7456 nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
7457 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007458
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007459 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007460
Johannes Bergbc43b282009-07-25 10:54:13 +02007461 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02007462 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02007463 GFP_ATOMIC);
7464 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007465
7466 return;
7467
7468nla_put_failure:
7469 genlmsg_cancel(msg, hdr);
7470 nlmsg_free(msg);
7471}
7472
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007473static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
7474 struct net_device *netdev,
7475 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007476 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007477{
7478 struct sk_buff *msg;
7479 void *hdr;
7480
Johannes Berge6d6e342009-07-01 21:26:47 +02007481 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007482 if (!msg)
7483 return;
7484
7485 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7486 if (!hdr) {
7487 nlmsg_free(msg);
7488 return;
7489 }
7490
David S. Miller9360ffd2012-03-29 04:41:26 -04007491 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7492 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7493 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
7494 goto nla_put_failure;
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007495
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007496 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007497
Johannes Berg463d0182009-07-14 00:33:35 +02007498 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7499 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007500 return;
7501
7502 nla_put_failure:
7503 genlmsg_cancel(msg, hdr);
7504 nlmsg_free(msg);
7505}
7506
7507void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007508 struct net_device *netdev, const u8 *buf,
7509 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007510{
7511 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007512 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007513}
7514
7515void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
7516 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007517 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007518{
Johannes Berge6d6e342009-07-01 21:26:47 +02007519 nl80211_send_mlme_event(rdev, netdev, buf, len,
7520 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007521}
7522
Jouni Malinen53b46b82009-03-27 20:53:56 +02007523void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007524 struct net_device *netdev, const u8 *buf,
7525 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007526{
7527 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007528 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007529}
7530
Jouni Malinen53b46b82009-03-27 20:53:56 +02007531void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
7532 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007533 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007534{
7535 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007536 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007537}
7538
Jouni Malinencf4e5942010-12-16 00:52:40 +02007539void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
7540 struct net_device *netdev, const u8 *buf,
7541 size_t len, gfp_t gfp)
7542{
7543 nl80211_send_mlme_event(rdev, netdev, buf, len,
7544 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
7545}
7546
7547void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
7548 struct net_device *netdev, const u8 *buf,
7549 size_t len, gfp_t gfp)
7550{
7551 nl80211_send_mlme_event(rdev, netdev, buf, len,
7552 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
7553}
7554
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04007555static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
7556 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02007557 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007558{
7559 struct sk_buff *msg;
7560 void *hdr;
7561
Johannes Berge6d6e342009-07-01 21:26:47 +02007562 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007563 if (!msg)
7564 return;
7565
7566 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7567 if (!hdr) {
7568 nlmsg_free(msg);
7569 return;
7570 }
7571
David S. Miller9360ffd2012-03-29 04:41:26 -04007572 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7573 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7574 nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
7575 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
7576 goto nla_put_failure;
Jouni Malinen1965c852009-04-22 21:38:25 +03007577
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007578 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03007579
Johannes Berg463d0182009-07-14 00:33:35 +02007580 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7581 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007582 return;
7583
7584 nla_put_failure:
7585 genlmsg_cancel(msg, hdr);
7586 nlmsg_free(msg);
7587}
7588
7589void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007590 struct net_device *netdev, const u8 *addr,
7591 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007592{
7593 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02007594 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007595}
7596
7597void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007598 struct net_device *netdev, const u8 *addr,
7599 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007600{
Johannes Berge6d6e342009-07-01 21:26:47 +02007601 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
7602 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007603}
7604
Samuel Ortizb23aa672009-07-01 21:26:54 +02007605void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
7606 struct net_device *netdev, const u8 *bssid,
7607 const u8 *req_ie, size_t req_ie_len,
7608 const u8 *resp_ie, size_t resp_ie_len,
7609 u16 status, gfp_t gfp)
7610{
7611 struct sk_buff *msg;
7612 void *hdr;
7613
7614 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7615 if (!msg)
7616 return;
7617
7618 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
7619 if (!hdr) {
7620 nlmsg_free(msg);
7621 return;
7622 }
7623
David S. Miller9360ffd2012-03-29 04:41:26 -04007624 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7625 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7626 (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
7627 nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
7628 (req_ie &&
7629 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
7630 (resp_ie &&
7631 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
7632 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02007633
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007634 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007635
Johannes Berg463d0182009-07-14 00:33:35 +02007636 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7637 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007638 return;
7639
7640 nla_put_failure:
7641 genlmsg_cancel(msg, hdr);
7642 nlmsg_free(msg);
7643
7644}
7645
7646void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
7647 struct net_device *netdev, const u8 *bssid,
7648 const u8 *req_ie, size_t req_ie_len,
7649 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
7650{
7651 struct sk_buff *msg;
7652 void *hdr;
7653
7654 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7655 if (!msg)
7656 return;
7657
7658 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
7659 if (!hdr) {
7660 nlmsg_free(msg);
7661 return;
7662 }
7663
David S. Miller9360ffd2012-03-29 04:41:26 -04007664 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7665 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7666 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) ||
7667 (req_ie &&
7668 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
7669 (resp_ie &&
7670 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
7671 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02007672
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007673 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007674
Johannes Berg463d0182009-07-14 00:33:35 +02007675 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7676 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007677 return;
7678
7679 nla_put_failure:
7680 genlmsg_cancel(msg, hdr);
7681 nlmsg_free(msg);
7682
7683}
7684
7685void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
7686 struct net_device *netdev, u16 reason,
Johannes Berg667503dd2009-07-07 03:56:11 +02007687 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02007688{
7689 struct sk_buff *msg;
7690 void *hdr;
7691
Johannes Berg667503dd2009-07-07 03:56:11 +02007692 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007693 if (!msg)
7694 return;
7695
7696 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
7697 if (!hdr) {
7698 nlmsg_free(msg);
7699 return;
7700 }
7701
David S. Miller9360ffd2012-03-29 04:41:26 -04007702 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7703 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7704 (from_ap && reason &&
7705 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) ||
7706 (from_ap &&
7707 nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) ||
7708 (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie)))
7709 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02007710
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007711 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007712
Johannes Berg463d0182009-07-14 00:33:35 +02007713 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7714 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007715 return;
7716
7717 nla_put_failure:
7718 genlmsg_cancel(msg, hdr);
7719 nlmsg_free(msg);
7720
7721}
7722
Johannes Berg04a773a2009-04-19 21:24:32 +02007723void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
7724 struct net_device *netdev, const u8 *bssid,
7725 gfp_t gfp)
7726{
7727 struct sk_buff *msg;
7728 void *hdr;
7729
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007730 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007731 if (!msg)
7732 return;
7733
7734 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
7735 if (!hdr) {
7736 nlmsg_free(msg);
7737 return;
7738 }
7739
David S. Miller9360ffd2012-03-29 04:41:26 -04007740 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7741 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7742 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
7743 goto nla_put_failure;
Johannes Berg04a773a2009-04-19 21:24:32 +02007744
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007745 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02007746
Johannes Berg463d0182009-07-14 00:33:35 +02007747 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7748 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007749 return;
7750
7751 nla_put_failure:
7752 genlmsg_cancel(msg, hdr);
7753 nlmsg_free(msg);
7754}
7755
Javier Cardonac93b5e72011-04-07 15:08:34 -07007756void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
7757 struct net_device *netdev,
7758 const u8 *macaddr, const u8* ie, u8 ie_len,
7759 gfp_t gfp)
7760{
7761 struct sk_buff *msg;
7762 void *hdr;
7763
7764 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7765 if (!msg)
7766 return;
7767
7768 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
7769 if (!hdr) {
7770 nlmsg_free(msg);
7771 return;
7772 }
7773
David S. Miller9360ffd2012-03-29 04:41:26 -04007774 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7775 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7776 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
7777 (ie_len && ie &&
7778 nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
7779 goto nla_put_failure;
Javier Cardonac93b5e72011-04-07 15:08:34 -07007780
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007781 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07007782
7783 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7784 nl80211_mlme_mcgrp.id, gfp);
7785 return;
7786
7787 nla_put_failure:
7788 genlmsg_cancel(msg, hdr);
7789 nlmsg_free(msg);
7790}
7791
Jouni Malinena3b8b052009-03-27 21:59:49 +02007792void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
7793 struct net_device *netdev, const u8 *addr,
7794 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02007795 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02007796{
7797 struct sk_buff *msg;
7798 void *hdr;
7799
Johannes Berge6d6e342009-07-01 21:26:47 +02007800 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007801 if (!msg)
7802 return;
7803
7804 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
7805 if (!hdr) {
7806 nlmsg_free(msg);
7807 return;
7808 }
7809
David S. Miller9360ffd2012-03-29 04:41:26 -04007810 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7811 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7812 (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
7813 nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) ||
7814 (key_id != -1 &&
7815 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) ||
7816 (tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc)))
7817 goto nla_put_failure;
Jouni Malinena3b8b052009-03-27 21:59:49 +02007818
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007819 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007820
Johannes Berg463d0182009-07-14 00:33:35 +02007821 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7822 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007823 return;
7824
7825 nla_put_failure:
7826 genlmsg_cancel(msg, hdr);
7827 nlmsg_free(msg);
7828}
7829
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007830void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
7831 struct ieee80211_channel *channel_before,
7832 struct ieee80211_channel *channel_after)
7833{
7834 struct sk_buff *msg;
7835 void *hdr;
7836 struct nlattr *nl_freq;
7837
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007838 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007839 if (!msg)
7840 return;
7841
7842 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
7843 if (!hdr) {
7844 nlmsg_free(msg);
7845 return;
7846 }
7847
7848 /*
7849 * Since we are applying the beacon hint to a wiphy we know its
7850 * wiphy_idx is valid
7851 */
David S. Miller9360ffd2012-03-29 04:41:26 -04007852 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
7853 goto nla_put_failure;
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007854
7855 /* Before */
7856 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
7857 if (!nl_freq)
7858 goto nla_put_failure;
7859 if (nl80211_msg_put_channel(msg, channel_before))
7860 goto nla_put_failure;
7861 nla_nest_end(msg, nl_freq);
7862
7863 /* After */
7864 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
7865 if (!nl_freq)
7866 goto nla_put_failure;
7867 if (nl80211_msg_put_channel(msg, channel_after))
7868 goto nla_put_failure;
7869 nla_nest_end(msg, nl_freq);
7870
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007871 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007872
Johannes Berg463d0182009-07-14 00:33:35 +02007873 rcu_read_lock();
7874 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
7875 GFP_ATOMIC);
7876 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007877
7878 return;
7879
7880nla_put_failure:
7881 genlmsg_cancel(msg, hdr);
7882 nlmsg_free(msg);
7883}
7884
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007885static void nl80211_send_remain_on_chan_event(
7886 int cmd, struct cfg80211_registered_device *rdev,
7887 struct net_device *netdev, u64 cookie,
7888 struct ieee80211_channel *chan,
7889 enum nl80211_channel_type channel_type,
7890 unsigned int duration, gfp_t gfp)
7891{
7892 struct sk_buff *msg;
7893 void *hdr;
7894
7895 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7896 if (!msg)
7897 return;
7898
7899 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7900 if (!hdr) {
7901 nlmsg_free(msg);
7902 return;
7903 }
7904
David S. Miller9360ffd2012-03-29 04:41:26 -04007905 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7906 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7907 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
7908 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) ||
7909 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
7910 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007911
David S. Miller9360ffd2012-03-29 04:41:26 -04007912 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL &&
7913 nla_put_u32(msg, NL80211_ATTR_DURATION, duration))
7914 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007915
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007916 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007917
7918 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7919 nl80211_mlme_mcgrp.id, gfp);
7920 return;
7921
7922 nla_put_failure:
7923 genlmsg_cancel(msg, hdr);
7924 nlmsg_free(msg);
7925}
7926
7927void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
7928 struct net_device *netdev, u64 cookie,
7929 struct ieee80211_channel *chan,
7930 enum nl80211_channel_type channel_type,
7931 unsigned int duration, gfp_t gfp)
7932{
7933 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
7934 rdev, netdev, cookie, chan,
7935 channel_type, duration, gfp);
7936}
7937
7938void nl80211_send_remain_on_channel_cancel(
7939 struct cfg80211_registered_device *rdev, struct net_device *netdev,
7940 u64 cookie, struct ieee80211_channel *chan,
7941 enum nl80211_channel_type channel_type, gfp_t gfp)
7942{
7943 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7944 rdev, netdev, cookie, chan,
7945 channel_type, 0, gfp);
7946}
7947
Johannes Berg98b62182009-12-23 13:15:44 +01007948void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
7949 struct net_device *dev, const u8 *mac_addr,
7950 struct station_info *sinfo, gfp_t gfp)
7951{
7952 struct sk_buff *msg;
7953
7954 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7955 if (!msg)
7956 return;
7957
John W. Linville66266b32012-03-15 13:25:41 -04007958 if (nl80211_send_station(msg, 0, 0, 0,
7959 rdev, dev, mac_addr, sinfo) < 0) {
Johannes Berg98b62182009-12-23 13:15:44 +01007960 nlmsg_free(msg);
7961 return;
7962 }
7963
7964 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7965 nl80211_mlme_mcgrp.id, gfp);
7966}
7967
Jouni Malinenec15e682011-03-23 15:29:52 +02007968void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
7969 struct net_device *dev, const u8 *mac_addr,
7970 gfp_t gfp)
7971{
7972 struct sk_buff *msg;
7973 void *hdr;
7974
7975 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7976 if (!msg)
7977 return;
7978
7979 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
7980 if (!hdr) {
7981 nlmsg_free(msg);
7982 return;
7983 }
7984
David S. Miller9360ffd2012-03-29 04:41:26 -04007985 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
7986 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
7987 goto nla_put_failure;
Jouni Malinenec15e682011-03-23 15:29:52 +02007988
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007989 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02007990
7991 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7992 nl80211_mlme_mcgrp.id, gfp);
7993 return;
7994
7995 nla_put_failure:
7996 genlmsg_cancel(msg, hdr);
7997 nlmsg_free(msg);
7998}
7999
Johannes Bergb92ab5d2011-11-04 11:18:19 +01008000static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
8001 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01008002{
8003 struct wireless_dev *wdev = dev->ieee80211_ptr;
8004 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
8005 struct sk_buff *msg;
8006 void *hdr;
8007 int err;
8008 u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
8009
8010 if (!nlpid)
8011 return false;
8012
8013 msg = nlmsg_new(100, gfp);
8014 if (!msg)
8015 return true;
8016
Johannes Bergb92ab5d2011-11-04 11:18:19 +01008017 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01008018 if (!hdr) {
8019 nlmsg_free(msg);
8020 return true;
8021 }
8022
David S. Miller9360ffd2012-03-29 04:41:26 -04008023 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8024 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
8025 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
8026 goto nla_put_failure;
Johannes Berg28946da2011-11-04 11:18:12 +01008027
8028 err = genlmsg_end(msg, hdr);
8029 if (err < 0) {
8030 nlmsg_free(msg);
8031 return true;
8032 }
8033
8034 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
8035 return true;
8036
8037 nla_put_failure:
8038 genlmsg_cancel(msg, hdr);
8039 nlmsg_free(msg);
8040 return true;
8041}
8042
Johannes Bergb92ab5d2011-11-04 11:18:19 +01008043bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
8044{
8045 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
8046 addr, gfp);
8047}
8048
8049bool nl80211_unexpected_4addr_frame(struct net_device *dev,
8050 const u8 *addr, gfp_t gfp)
8051{
8052 return __nl80211_unexpected_frame(dev,
8053 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
8054 addr, gfp);
8055}
8056
Johannes Berg2e161f72010-08-12 15:38:38 +02008057int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
8058 struct net_device *netdev, u32 nlpid,
Johannes Berg804483e2012-03-05 22:18:41 +01008059 int freq, int sig_dbm,
8060 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02008061{
8062 struct sk_buff *msg;
8063 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02008064
8065 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
8066 if (!msg)
8067 return -ENOMEM;
8068
Johannes Berg2e161f72010-08-12 15:38:38 +02008069 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02008070 if (!hdr) {
8071 nlmsg_free(msg);
8072 return -ENOMEM;
8073 }
8074
David S. Miller9360ffd2012-03-29 04:41:26 -04008075 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8076 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8077 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
8078 (sig_dbm &&
8079 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
8080 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
8081 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02008082
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008083 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02008084
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008085 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
Jouni Malinen026331c2010-02-15 12:53:10 +02008086
8087 nla_put_failure:
8088 genlmsg_cancel(msg, hdr);
8089 nlmsg_free(msg);
8090 return -ENOBUFS;
8091}
8092
Johannes Berg2e161f72010-08-12 15:38:38 +02008093void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
8094 struct net_device *netdev, u64 cookie,
8095 const u8 *buf, size_t len, bool ack,
8096 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02008097{
8098 struct sk_buff *msg;
8099 void *hdr;
8100
8101 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
8102 if (!msg)
8103 return;
8104
Johannes Berg2e161f72010-08-12 15:38:38 +02008105 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02008106 if (!hdr) {
8107 nlmsg_free(msg);
8108 return;
8109 }
8110
David S. Miller9360ffd2012-03-29 04:41:26 -04008111 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8112 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8113 nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
8114 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
8115 (ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
8116 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02008117
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008118 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02008119
8120 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
8121 return;
8122
8123 nla_put_failure:
8124 genlmsg_cancel(msg, hdr);
8125 nlmsg_free(msg);
8126}
8127
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008128void
8129nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
8130 struct net_device *netdev,
8131 enum nl80211_cqm_rssi_threshold_event rssi_event,
8132 gfp_t gfp)
8133{
8134 struct sk_buff *msg;
8135 struct nlattr *pinfoattr;
8136 void *hdr;
8137
8138 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8139 if (!msg)
8140 return;
8141
8142 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
8143 if (!hdr) {
8144 nlmsg_free(msg);
8145 return;
8146 }
8147
David S. Miller9360ffd2012-03-29 04:41:26 -04008148 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8149 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
8150 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008151
8152 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
8153 if (!pinfoattr)
8154 goto nla_put_failure;
8155
David S. Miller9360ffd2012-03-29 04:41:26 -04008156 if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
8157 rssi_event))
8158 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008159
8160 nla_nest_end(msg, pinfoattr);
8161
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008162 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008163
8164 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8165 nl80211_mlme_mcgrp.id, gfp);
8166 return;
8167
8168 nla_put_failure:
8169 genlmsg_cancel(msg, hdr);
8170 nlmsg_free(msg);
8171}
8172
Johannes Berge5497d72011-07-05 16:35:40 +02008173void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
8174 struct net_device *netdev, const u8 *bssid,
8175 const u8 *replay_ctr, gfp_t gfp)
8176{
8177 struct sk_buff *msg;
8178 struct nlattr *rekey_attr;
8179 void *hdr;
8180
8181 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8182 if (!msg)
8183 return;
8184
8185 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
8186 if (!hdr) {
8187 nlmsg_free(msg);
8188 return;
8189 }
8190
David S. Miller9360ffd2012-03-29 04:41:26 -04008191 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8192 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8193 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
8194 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +02008195
8196 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
8197 if (!rekey_attr)
8198 goto nla_put_failure;
8199
David S. Miller9360ffd2012-03-29 04:41:26 -04008200 if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR,
8201 NL80211_REPLAY_CTR_LEN, replay_ctr))
8202 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +02008203
8204 nla_nest_end(msg, rekey_attr);
8205
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008206 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02008207
8208 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8209 nl80211_mlme_mcgrp.id, gfp);
8210 return;
8211
8212 nla_put_failure:
8213 genlmsg_cancel(msg, hdr);
8214 nlmsg_free(msg);
8215}
8216
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008217void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
8218 struct net_device *netdev, int index,
8219 const u8 *bssid, bool preauth, gfp_t gfp)
8220{
8221 struct sk_buff *msg;
8222 struct nlattr *attr;
8223 void *hdr;
8224
8225 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8226 if (!msg)
8227 return;
8228
8229 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
8230 if (!hdr) {
8231 nlmsg_free(msg);
8232 return;
8233 }
8234
David S. Miller9360ffd2012-03-29 04:41:26 -04008235 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8236 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
8237 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008238
8239 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
8240 if (!attr)
8241 goto nla_put_failure;
8242
David S. Miller9360ffd2012-03-29 04:41:26 -04008243 if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) ||
8244 nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) ||
8245 (preauth &&
8246 nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH)))
8247 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008248
8249 nla_nest_end(msg, attr);
8250
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008251 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008252
8253 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8254 nl80211_mlme_mcgrp.id, gfp);
8255 return;
8256
8257 nla_put_failure:
8258 genlmsg_cancel(msg, hdr);
8259 nlmsg_free(msg);
8260}
8261
Thomas Pedersen53145262012-04-06 13:35:47 -07008262void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
8263 struct net_device *netdev, int freq,
8264 enum nl80211_channel_type type, gfp_t gfp)
8265{
8266 struct sk_buff *msg;
8267 void *hdr;
8268
8269 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8270 if (!msg)
8271 return;
8272
8273 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
8274 if (!hdr) {
8275 nlmsg_free(msg);
8276 return;
8277 }
8278
John W. Linville7eab0f62012-04-12 14:25:14 -04008279 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8280 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
8281 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type))
8282 goto nla_put_failure;
Thomas Pedersen53145262012-04-06 13:35:47 -07008283
8284 genlmsg_end(msg, hdr);
8285
8286 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8287 nl80211_mlme_mcgrp.id, gfp);
8288 return;
8289
8290 nla_put_failure:
8291 genlmsg_cancel(msg, hdr);
8292 nlmsg_free(msg);
8293}
8294
Johannes Bergc063dbf2010-11-24 08:10:05 +01008295void
8296nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
8297 struct net_device *netdev, const u8 *peer,
8298 u32 num_packets, gfp_t gfp)
8299{
8300 struct sk_buff *msg;
8301 struct nlattr *pinfoattr;
8302 void *hdr;
8303
8304 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8305 if (!msg)
8306 return;
8307
8308 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
8309 if (!hdr) {
8310 nlmsg_free(msg);
8311 return;
8312 }
8313
David S. Miller9360ffd2012-03-29 04:41:26 -04008314 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8315 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8316 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
8317 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +01008318
8319 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
8320 if (!pinfoattr)
8321 goto nla_put_failure;
8322
David S. Miller9360ffd2012-03-29 04:41:26 -04008323 if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
8324 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +01008325
8326 nla_nest_end(msg, pinfoattr);
8327
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008328 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01008329
8330 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8331 nl80211_mlme_mcgrp.id, gfp);
8332 return;
8333
8334 nla_put_failure:
8335 genlmsg_cancel(msg, hdr);
8336 nlmsg_free(msg);
8337}
8338
Johannes Berg7f6cf312011-11-04 11:18:15 +01008339void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
8340 u64 cookie, bool acked, gfp_t gfp)
8341{
8342 struct wireless_dev *wdev = dev->ieee80211_ptr;
8343 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
8344 struct sk_buff *msg;
8345 void *hdr;
8346 int err;
8347
8348 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8349 if (!msg)
8350 return;
8351
8352 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
8353 if (!hdr) {
8354 nlmsg_free(msg);
8355 return;
8356 }
8357
David S. Miller9360ffd2012-03-29 04:41:26 -04008358 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8359 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
8360 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
8361 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
8362 (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
8363 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01008364
8365 err = genlmsg_end(msg, hdr);
8366 if (err < 0) {
8367 nlmsg_free(msg);
8368 return;
8369 }
8370
8371 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8372 nl80211_mlme_mcgrp.id, gfp);
8373 return;
8374
8375 nla_put_failure:
8376 genlmsg_cancel(msg, hdr);
8377 nlmsg_free(msg);
8378}
8379EXPORT_SYMBOL(cfg80211_probe_status);
8380
Johannes Berg5e7602302011-11-04 11:18:17 +01008381void cfg80211_report_obss_beacon(struct wiphy *wiphy,
8382 const u8 *frame, size_t len,
Johannes Berg804483e2012-03-05 22:18:41 +01008383 int freq, int sig_dbm, gfp_t gfp)
Johannes Berg5e7602302011-11-04 11:18:17 +01008384{
8385 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
8386 struct sk_buff *msg;
8387 void *hdr;
8388 u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
8389
8390 if (!nlpid)
8391 return;
8392
8393 msg = nlmsg_new(len + 100, gfp);
8394 if (!msg)
8395 return;
8396
8397 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
8398 if (!hdr) {
8399 nlmsg_free(msg);
8400 return;
8401 }
8402
David S. Miller9360ffd2012-03-29 04:41:26 -04008403 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8404 (freq &&
8405 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
8406 (sig_dbm &&
8407 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
8408 nla_put(msg, NL80211_ATTR_FRAME, len, frame))
8409 goto nla_put_failure;
Johannes Berg5e7602302011-11-04 11:18:17 +01008410
8411 genlmsg_end(msg, hdr);
8412
8413 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
8414 return;
8415
8416 nla_put_failure:
8417 genlmsg_cancel(msg, hdr);
8418 nlmsg_free(msg);
8419}
8420EXPORT_SYMBOL(cfg80211_report_obss_beacon);
8421
Jouni Malinen026331c2010-02-15 12:53:10 +02008422static int nl80211_netlink_notify(struct notifier_block * nb,
8423 unsigned long state,
8424 void *_notify)
8425{
8426 struct netlink_notify *notify = _notify;
8427 struct cfg80211_registered_device *rdev;
8428 struct wireless_dev *wdev;
8429
8430 if (state != NETLINK_URELEASE)
8431 return NOTIFY_DONE;
8432
8433 rcu_read_lock();
8434
Johannes Berg5e7602302011-11-04 11:18:17 +01008435 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +02008436 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02008437 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Johannes Berg5e7602302011-11-04 11:18:17 +01008438 if (rdev->ap_beacons_nlpid == notify->pid)
8439 rdev->ap_beacons_nlpid = 0;
8440 }
Jouni Malinen026331c2010-02-15 12:53:10 +02008441
8442 rcu_read_unlock();
8443
8444 return NOTIFY_DONE;
8445}
8446
8447static struct notifier_block nl80211_netlink_notifier = {
8448 .notifier_call = nl80211_netlink_notify,
8449};
8450
Johannes Berg55682962007-09-20 13:09:35 -04008451/* initialisation/exit functions */
8452
8453int nl80211_init(void)
8454{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008455 int err;
Johannes Berg55682962007-09-20 13:09:35 -04008456
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008457 err = genl_register_family_with_ops(&nl80211_fam,
8458 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04008459 if (err)
8460 return err;
8461
Johannes Berg55682962007-09-20 13:09:35 -04008462 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
8463 if (err)
8464 goto err_out;
8465
Johannes Berg2a519312009-02-10 21:25:55 +01008466 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
8467 if (err)
8468 goto err_out;
8469
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008470 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
8471 if (err)
8472 goto err_out;
8473
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008474 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
8475 if (err)
8476 goto err_out;
8477
Johannes Bergaff89a92009-07-01 21:26:51 +02008478#ifdef CONFIG_NL80211_TESTMODE
8479 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
8480 if (err)
8481 goto err_out;
8482#endif
8483
Jouni Malinen026331c2010-02-15 12:53:10 +02008484 err = netlink_register_notifier(&nl80211_netlink_notifier);
8485 if (err)
8486 goto err_out;
8487
Johannes Berg55682962007-09-20 13:09:35 -04008488 return 0;
8489 err_out:
8490 genl_unregister_family(&nl80211_fam);
8491 return err;
8492}
8493
8494void nl80211_exit(void)
8495{
Jouni Malinen026331c2010-02-15 12:53:10 +02008496 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04008497 genl_unregister_family(&nl80211_fam);
8498}