blob: a363ca17bfc572d81bb115da5d302416740bca93 [file] [log] [blame]
Johannes Berg55682962007-09-20 13:09:35 -04001/*
2 * This is the new netlink-based wireless configuration interface.
3 *
Jouni Malinen026331c2010-02-15 12:53:10 +02004 * Copyright 2006-2010 Johannes Berg <johannes@sipsolutions.net>
Johannes Berg55682962007-09-20 13:09:35 -04005 */
6
7#include <linux/if.h>
8#include <linux/module.h>
9#include <linux/err.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090010#include <linux/slab.h>
Johannes Berg55682962007-09-20 13:09:35 -040011#include <linux/list.h>
12#include <linux/if_ether.h>
13#include <linux/ieee80211.h>
14#include <linux/nl80211.h>
15#include <linux/rtnetlink.h>
16#include <linux/netlink.h>
Johannes Berg2a519312009-02-10 21:25:55 +010017#include <linux/etherdevice.h>
Johannes Berg463d0182009-07-14 00:33:35 +020018#include <net/net_namespace.h>
Johannes Berg55682962007-09-20 13:09:35 -040019#include <net/genetlink.h>
20#include <net/cfg80211.h>
Johannes Berg463d0182009-07-14 00:33:35 +020021#include <net/sock.h>
Johannes Berg55682962007-09-20 13:09:35 -040022#include "core.h"
23#include "nl80211.h"
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -070024#include "reg.h"
Johannes Berg55682962007-09-20 13:09:35 -040025
Jouni Malinen5fb628e2011-08-10 23:54:35 +030026static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type);
27static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
28 struct genl_info *info,
29 struct cfg80211_crypto_settings *settings,
30 int cipher_limit);
31
Johannes Berg4c476992010-10-04 21:36:35 +020032static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
33 struct genl_info *info);
34static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
35 struct genl_info *info);
36
Johannes Berg55682962007-09-20 13:09:35 -040037/* the netlink family */
38static struct genl_family nl80211_fam = {
39 .id = GENL_ID_GENERATE, /* don't bother with a hardcoded ID */
40 .name = "nl80211", /* have users key off the name instead */
41 .hdrsize = 0, /* no private header */
42 .version = 1, /* no particular meaning now */
43 .maxattr = NL80211_ATTR_MAX,
Johannes Berg463d0182009-07-14 00:33:35 +020044 .netnsok = true,
Johannes Berg4c476992010-10-04 21:36:35 +020045 .pre_doit = nl80211_pre_doit,
46 .post_doit = nl80211_post_doit,
Johannes Berg55682962007-09-20 13:09:35 -040047};
48
Johannes Berg79c97e92009-07-07 03:56:12 +020049/* internal helper: get rdev and dev */
Johannes Berg00918d32011-12-13 17:22:05 +010050static int get_rdev_dev_by_ifindex(struct net *netns, struct nlattr **attrs,
51 struct cfg80211_registered_device **rdev,
52 struct net_device **dev)
Johannes Berg55682962007-09-20 13:09:35 -040053{
54 int ifindex;
55
Johannes Bergbba95fe2008-07-29 13:22:51 +020056 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040057 return -EINVAL;
58
Johannes Bergbba95fe2008-07-29 13:22:51 +020059 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg00918d32011-12-13 17:22:05 +010060 *dev = dev_get_by_index(netns, ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040061 if (!*dev)
62 return -ENODEV;
63
Johannes Berg00918d32011-12-13 17:22:05 +010064 *rdev = cfg80211_get_dev_from_ifindex(netns, ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020065 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040066 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020067 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040068 }
69
70 return 0;
71}
72
73/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000074static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040075 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
76 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070077 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020078 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020079 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053080 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020081 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
82 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
83 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
84 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010085 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040086
87 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
88 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
89 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010090
Eliad Pellere007b852011-11-24 18:13:56 +020091 [NL80211_ATTR_MAC] = { .len = ETH_ALEN },
92 [NL80211_ATTR_PREV_BSSID] = { .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010093
Johannes Bergb9454e82009-07-08 13:29:08 +020094 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010095 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
96 .len = WLAN_MAX_KEY_LEN },
97 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
98 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
99 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen81962262011-11-02 23:36:31 +0200100 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200101 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100102
103 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
104 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
105 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
106 .len = IEEE80211_MAX_DATA_LEN },
107 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
108 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100109 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
110 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
111 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
112 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
113 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100114 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100115 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200116 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100117 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +0800118 .len = IEEE80211_MAX_MESH_ID_LEN },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100119 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300120
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700121 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
122 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
123
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300124 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
125 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
126 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200127 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
128 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100129 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc92008-08-25 11:58:58 +0300130
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800131 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700132 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700133
Johannes Berg6c739412011-11-03 09:27:01 +0100134 [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200135
136 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
137 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
138 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100139 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
140 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200141
142 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
143 .len = IEEE80211_MAX_SSID_LEN },
144 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
145 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200146 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300147 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382ce2009-05-06 22:09:37 +0300148 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300149 [NL80211_ATTR_STA_FLAGS2] = {
150 .len = sizeof(struct nl80211_sta_flag_update),
151 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300152 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300153 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
154 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200155 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
156 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
157 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200158 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100159 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100160 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
161 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100162 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
163 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200164 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200165 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
166 .len = IEEE80211_MAX_DATA_LEN },
167 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200168 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200169 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300170 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200171 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300172 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
173 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f782010-08-12 15:38:38 +0200174 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900175 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
176 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100177 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100178 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100179 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200180 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700181 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300182 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200183 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200184 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300185 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Jouni Malinen9946ecf2011-08-10 23:55:56 +0300186 [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
187 .len = IEEE80211_MAX_DATA_LEN },
188 [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
189 .len = IEEE80211_MAX_DATA_LEN },
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530190 [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300191 [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530192 [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
Arik Nemtsov109086c2011-09-28 14:12:50 +0300193 [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
194 [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
195 [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
196 [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
197 [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
Johannes Berge247bd902011-11-04 11:18:21 +0100198 [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
Arik Nemtsov00f740e2011-11-10 11:28:56 +0200199 [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
200 .len = IEEE80211_MAX_DATA_LEN },
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -0700201 [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
Ben Greear7e7c8922011-11-18 11:31:59 -0800202 [NL80211_ATTR_DISABLE_HT] = { .type = NLA_FLAG },
203 [NL80211_ATTR_HT_CAPABILITY_MASK] = {
204 .len = NL80211_HT_CAPABILITY_LEN
205 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100206 [NL80211_ATTR_NOACK_MAP] = { .type = NLA_U16 },
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +0530207 [NL80211_ATTR_INACTIVITY_TIMEOUT] = { .type = NLA_U16 },
Bala Shanmugam4486ea92012-03-07 17:27:12 +0530208 [NL80211_ATTR_BG_SCAN_PERIOD] = { .type = NLA_U16 },
Johannes Berg55682962007-09-20 13:09:35 -0400209};
210
Johannes Berge31b8212010-10-05 19:39:30 +0200211/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000212static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200213 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200214 [NL80211_KEY_IDX] = { .type = NLA_U8 },
215 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200216 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200217 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
218 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200219 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100220 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
221};
222
223/* policy for the key default flags */
224static const struct nla_policy
225nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
226 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
227 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200228};
229
Johannes Bergff1b6e62011-05-04 15:37:28 +0200230/* policy for WoWLAN attributes */
231static const struct nla_policy
232nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
233 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
234 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
235 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
236 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200237 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
238 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
239 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
240 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200241};
242
Johannes Berge5497d72011-07-05 16:35:40 +0200243/* policy for GTK rekey offload attributes */
244static const struct nla_policy
245nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
246 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
247 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
248 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
249};
250
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300251static const struct nla_policy
252nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
Johannes Berg4a4ab0d2012-06-13 11:17:11 +0200253 [NL80211_SCHED_SCAN_MATCH_ATTR_SSID] = { .type = NLA_BINARY,
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300254 .len = IEEE80211_MAX_SSID_LEN },
255};
256
Holger Schuriga0438972009-11-11 11:30:02 +0100257/* ifidx get helper */
258static int nl80211_get_ifidx(struct netlink_callback *cb)
259{
260 int res;
261
262 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
263 nl80211_fam.attrbuf, nl80211_fam.maxattr,
264 nl80211_policy);
265 if (res)
266 return res;
267
268 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
269 return -EINVAL;
270
271 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
272 if (!res)
273 return -EINVAL;
274 return res;
275}
276
Johannes Berg67748892010-10-04 21:14:06 +0200277static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
278 struct netlink_callback *cb,
279 struct cfg80211_registered_device **rdev,
280 struct net_device **dev)
281{
282 int ifidx = cb->args[0];
283 int err;
284
285 if (!ifidx)
286 ifidx = nl80211_get_ifidx(cb);
287 if (ifidx < 0)
288 return ifidx;
289
290 cb->args[0] = ifidx;
291
292 rtnl_lock();
293
294 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
295 if (!*dev) {
296 err = -ENODEV;
297 goto out_rtnl;
298 }
299
300 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100301 if (IS_ERR(*rdev)) {
302 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200303 goto out_rtnl;
304 }
305
306 return 0;
307 out_rtnl:
308 rtnl_unlock();
309 return err;
310}
311
312static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
313{
314 cfg80211_unlock_rdev(rdev);
315 rtnl_unlock();
316}
317
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100318/* IE validation */
319static bool is_valid_ie_attr(const struct nlattr *attr)
320{
321 const u8 *pos;
322 int len;
323
324 if (!attr)
325 return true;
326
327 pos = nla_data(attr);
328 len = nla_len(attr);
329
330 while (len) {
331 u8 elemlen;
332
333 if (len < 2)
334 return false;
335 len -= 2;
336
337 elemlen = pos[1];
338 if (elemlen > len)
339 return false;
340
341 len -= elemlen;
342 pos += 2 + elemlen;
343 }
344
345 return true;
346}
347
Johannes Berg55682962007-09-20 13:09:35 -0400348/* message building helper */
349static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
350 int flags, u8 cmd)
351{
352 /* since there is no private header just add the generic one */
353 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
354}
355
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400356static int nl80211_msg_put_channel(struct sk_buff *msg,
357 struct ieee80211_channel *chan)
358{
David S. Miller9360ffd2012-03-29 04:41:26 -0400359 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_FREQ,
360 chan->center_freq))
361 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400362
David S. Miller9360ffd2012-03-29 04:41:26 -0400363 if ((chan->flags & IEEE80211_CHAN_DISABLED) &&
364 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_DISABLED))
365 goto nla_put_failure;
366 if ((chan->flags & IEEE80211_CHAN_PASSIVE_SCAN) &&
367 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN))
368 goto nla_put_failure;
369 if ((chan->flags & IEEE80211_CHAN_NO_IBSS) &&
370 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_NO_IBSS))
371 goto nla_put_failure;
372 if ((chan->flags & IEEE80211_CHAN_RADAR) &&
373 nla_put_flag(msg, NL80211_FREQUENCY_ATTR_RADAR))
374 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400375
David S. Miller9360ffd2012-03-29 04:41:26 -0400376 if (nla_put_u32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
377 DBM_TO_MBM(chan->max_power)))
378 goto nla_put_failure;
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400379
380 return 0;
381
382 nla_put_failure:
383 return -ENOBUFS;
384}
385
Johannes Berg55682962007-09-20 13:09:35 -0400386/* netlink command implementations */
387
Johannes Bergb9454e82009-07-08 13:29:08 +0200388struct key_parse {
389 struct key_params p;
390 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200391 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200392 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100393 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200394};
395
396static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
397{
398 struct nlattr *tb[NL80211_KEY_MAX + 1];
399 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
400 nl80211_key_policy);
401 if (err)
402 return err;
403
404 k->def = !!tb[NL80211_KEY_DEFAULT];
405 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
406
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100407 if (k->def) {
408 k->def_uni = true;
409 k->def_multi = true;
410 }
411 if (k->defmgmt)
412 k->def_multi = true;
413
Johannes Bergb9454e82009-07-08 13:29:08 +0200414 if (tb[NL80211_KEY_IDX])
415 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
416
417 if (tb[NL80211_KEY_DATA]) {
418 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
419 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
420 }
421
422 if (tb[NL80211_KEY_SEQ]) {
423 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
424 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
425 }
426
427 if (tb[NL80211_KEY_CIPHER])
428 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
429
Johannes Berge31b8212010-10-05 19:39:30 +0200430 if (tb[NL80211_KEY_TYPE]) {
431 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
432 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
433 return -EINVAL;
434 }
435
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100436 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
437 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
Johannes Berg2da8f412012-01-20 13:52:37 +0100438 err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
439 tb[NL80211_KEY_DEFAULT_TYPES],
440 nl80211_key_default_policy);
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100441 if (err)
442 return err;
443
444 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
445 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
446 }
447
Johannes Bergb9454e82009-07-08 13:29:08 +0200448 return 0;
449}
450
451static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
452{
453 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
454 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
455 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
456 }
457
458 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
459 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
460 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
461 }
462
463 if (info->attrs[NL80211_ATTR_KEY_IDX])
464 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
465
466 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
467 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
468
469 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
470 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
471
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100472 if (k->def) {
473 k->def_uni = true;
474 k->def_multi = true;
475 }
476 if (k->defmgmt)
477 k->def_multi = true;
478
Johannes Berge31b8212010-10-05 19:39:30 +0200479 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
480 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
481 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
482 return -EINVAL;
483 }
484
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100485 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
486 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
487 int err = nla_parse_nested(
488 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
489 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
490 nl80211_key_default_policy);
491 if (err)
492 return err;
493
494 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
495 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
496 }
497
Johannes Bergb9454e82009-07-08 13:29:08 +0200498 return 0;
499}
500
501static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
502{
503 int err;
504
505 memset(k, 0, sizeof(*k));
506 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200507 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200508
509 if (info->attrs[NL80211_ATTR_KEY])
510 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
511 else
512 err = nl80211_parse_key_old(info, k);
513
514 if (err)
515 return err;
516
517 if (k->def && k->defmgmt)
518 return -EINVAL;
519
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100520 if (k->defmgmt) {
521 if (k->def_uni || !k->def_multi)
522 return -EINVAL;
523 }
524
Johannes Bergb9454e82009-07-08 13:29:08 +0200525 if (k->idx != -1) {
526 if (k->defmgmt) {
527 if (k->idx < 4 || k->idx > 5)
528 return -EINVAL;
529 } else if (k->def) {
530 if (k->idx < 0 || k->idx > 3)
531 return -EINVAL;
532 } else {
533 if (k->idx < 0 || k->idx > 5)
534 return -EINVAL;
535 }
536 }
537
538 return 0;
539}
540
Johannes Bergfffd0932009-07-08 14:22:54 +0200541static struct cfg80211_cached_keys *
542nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
543 struct nlattr *keys)
544{
545 struct key_parse parse;
546 struct nlattr *key;
547 struct cfg80211_cached_keys *result;
548 int rem, err, def = 0;
549
550 result = kzalloc(sizeof(*result), GFP_KERNEL);
551 if (!result)
552 return ERR_PTR(-ENOMEM);
553
554 result->def = -1;
555 result->defmgmt = -1;
556
557 nla_for_each_nested(key, keys, rem) {
558 memset(&parse, 0, sizeof(parse));
559 parse.idx = -1;
560
561 err = nl80211_parse_key_new(key, &parse);
562 if (err)
563 goto error;
564 err = -EINVAL;
565 if (!parse.p.key)
566 goto error;
567 if (parse.idx < 0 || parse.idx > 4)
568 goto error;
569 if (parse.def) {
570 if (def)
571 goto error;
572 def = 1;
573 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100574 if (!parse.def_uni || !parse.def_multi)
575 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200576 } else if (parse.defmgmt)
577 goto error;
578 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200579 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200580 if (err)
581 goto error;
582 result->params[parse.idx].cipher = parse.p.cipher;
583 result->params[parse.idx].key_len = parse.p.key_len;
584 result->params[parse.idx].key = result->data[parse.idx];
585 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
586 }
587
588 return result;
589 error:
590 kfree(result);
591 return ERR_PTR(err);
592}
593
594static int nl80211_key_allowed(struct wireless_dev *wdev)
595{
596 ASSERT_WDEV_LOCK(wdev);
597
Johannes Bergfffd0932009-07-08 14:22:54 +0200598 switch (wdev->iftype) {
599 case NL80211_IFTYPE_AP:
600 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200601 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700602 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200603 break;
604 case NL80211_IFTYPE_ADHOC:
605 if (!wdev->current_bss)
606 return -ENOLINK;
607 break;
608 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200609 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200610 if (wdev->sme_state != CFG80211_SME_CONNECTED)
611 return -ENOLINK;
612 break;
613 default:
614 return -EINVAL;
615 }
616
617 return 0;
618}
619
Johannes Berg7527a782011-05-13 10:58:57 +0200620static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
621{
622 struct nlattr *nl_modes = nla_nest_start(msg, attr);
623 int i;
624
625 if (!nl_modes)
626 goto nla_put_failure;
627
628 i = 0;
629 while (ifmodes) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400630 if ((ifmodes & 1) && nla_put_flag(msg, i))
631 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200632 ifmodes >>= 1;
633 i++;
634 }
635
636 nla_nest_end(msg, nl_modes);
637 return 0;
638
639nla_put_failure:
640 return -ENOBUFS;
641}
642
643static int nl80211_put_iface_combinations(struct wiphy *wiphy,
644 struct sk_buff *msg)
645{
646 struct nlattr *nl_combis;
647 int i, j;
648
649 nl_combis = nla_nest_start(msg,
650 NL80211_ATTR_INTERFACE_COMBINATIONS);
651 if (!nl_combis)
652 goto nla_put_failure;
653
654 for (i = 0; i < wiphy->n_iface_combinations; i++) {
655 const struct ieee80211_iface_combination *c;
656 struct nlattr *nl_combi, *nl_limits;
657
658 c = &wiphy->iface_combinations[i];
659
660 nl_combi = nla_nest_start(msg, i + 1);
661 if (!nl_combi)
662 goto nla_put_failure;
663
664 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
665 if (!nl_limits)
666 goto nla_put_failure;
667
668 for (j = 0; j < c->n_limits; j++) {
669 struct nlattr *nl_limit;
670
671 nl_limit = nla_nest_start(msg, j + 1);
672 if (!nl_limit)
673 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -0400674 if (nla_put_u32(msg, NL80211_IFACE_LIMIT_MAX,
675 c->limits[j].max))
676 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200677 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
678 c->limits[j].types))
679 goto nla_put_failure;
680 nla_nest_end(msg, nl_limit);
681 }
682
683 nla_nest_end(msg, nl_limits);
684
David S. Miller9360ffd2012-03-29 04:41:26 -0400685 if (c->beacon_int_infra_match &&
686 nla_put_flag(msg, NL80211_IFACE_COMB_STA_AP_BI_MATCH))
687 goto nla_put_failure;
688 if (nla_put_u32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
689 c->num_different_channels) ||
690 nla_put_u32(msg, NL80211_IFACE_COMB_MAXNUM,
691 c->max_interfaces))
692 goto nla_put_failure;
Johannes Berg7527a782011-05-13 10:58:57 +0200693
694 nla_nest_end(msg, nl_combi);
695 }
696
697 nla_nest_end(msg, nl_combis);
698
699 return 0;
700nla_put_failure:
701 return -ENOBUFS;
702}
703
Johannes Berg55682962007-09-20 13:09:35 -0400704static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
705 struct cfg80211_registered_device *dev)
706{
707 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100708 struct nlattr *nl_bands, *nl_band;
709 struct nlattr *nl_freqs, *nl_freq;
710 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100711 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100712 enum ieee80211_band band;
713 struct ieee80211_channel *chan;
714 struct ieee80211_rate *rate;
715 int i;
Johannes Berg2e161f782010-08-12 15:38:38 +0200716 const struct ieee80211_txrx_stypes *mgmt_stypes =
717 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400718
719 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
720 if (!hdr)
721 return -1;
722
David S. Miller9360ffd2012-03-29 04:41:26 -0400723 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx) ||
724 nla_put_string(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)) ||
725 nla_put_u32(msg, NL80211_ATTR_GENERATION,
726 cfg80211_rdev_list_generation) ||
727 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
728 dev->wiphy.retry_short) ||
729 nla_put_u8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
730 dev->wiphy.retry_long) ||
731 nla_put_u32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
732 dev->wiphy.frag_threshold) ||
733 nla_put_u32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
734 dev->wiphy.rts_threshold) ||
735 nla_put_u8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
736 dev->wiphy.coverage_class) ||
737 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
738 dev->wiphy.max_scan_ssids) ||
739 nla_put_u8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
740 dev->wiphy.max_sched_scan_ssids) ||
741 nla_put_u16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
742 dev->wiphy.max_scan_ie_len) ||
743 nla_put_u16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
744 dev->wiphy.max_sched_scan_ie_len) ||
745 nla_put_u8(msg, NL80211_ATTR_MAX_MATCH_SETS,
746 dev->wiphy.max_match_sets))
747 goto nla_put_failure;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200748
David S. Miller9360ffd2012-03-29 04:41:26 -0400749 if ((dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN) &&
750 nla_put_flag(msg, NL80211_ATTR_SUPPORT_IBSS_RSN))
751 goto nla_put_failure;
752 if ((dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH) &&
753 nla_put_flag(msg, NL80211_ATTR_SUPPORT_MESH_AUTH))
754 goto nla_put_failure;
755 if ((dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
756 nla_put_flag(msg, NL80211_ATTR_SUPPORT_AP_UAPSD))
757 goto nla_put_failure;
758 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM) &&
759 nla_put_flag(msg, NL80211_ATTR_ROAM_SUPPORT))
760 goto nla_put_failure;
761 if ((dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
762 nla_put_flag(msg, NL80211_ATTR_TDLS_SUPPORT))
763 goto nla_put_failure;
764 if ((dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP) &&
765 nla_put_flag(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP))
766 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +0200767
David S. Miller9360ffd2012-03-29 04:41:26 -0400768 if (nla_put(msg, NL80211_ATTR_CIPHER_SUITES,
769 sizeof(u32) * dev->wiphy.n_cipher_suites,
770 dev->wiphy.cipher_suites))
771 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +0100772
David S. Miller9360ffd2012-03-29 04:41:26 -0400773 if (nla_put_u8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
774 dev->wiphy.max_num_pmkids))
775 goto nla_put_failure;
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530776
David S. Miller9360ffd2012-03-29 04:41:26 -0400777 if ((dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
778 nla_put_flag(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE))
779 goto nla_put_failure;
Johannes Berg25e47c182009-04-02 20:14:06 +0200780
David S. Miller9360ffd2012-03-29 04:41:26 -0400781 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
782 dev->wiphy.available_antennas_tx) ||
783 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
784 dev->wiphy.available_antennas_rx))
785 goto nla_put_failure;
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100786
David S. Miller9360ffd2012-03-29 04:41:26 -0400787 if ((dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD) &&
788 nla_put_u32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
789 dev->wiphy.probe_resp_offload))
790 goto nla_put_failure;
Arik Nemtsov87bbbe22011-11-10 11:28:55 +0200791
Bruno Randolf7f531e02010-12-16 11:30:22 +0900792 if ((dev->wiphy.available_antennas_tx ||
793 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900794 u32 tx_ant = 0, rx_ant = 0;
795 int res;
796 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
797 if (!res) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400798 if (nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX,
799 tx_ant) ||
800 nla_put_u32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX,
801 rx_ant))
802 goto nla_put_failure;
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900803 }
804 }
805
Johannes Berg7527a782011-05-13 10:58:57 +0200806 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
807 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700808 goto nla_put_failure;
809
Johannes Bergee688b002008-01-24 19:38:39 +0100810 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
811 if (!nl_bands)
812 goto nla_put_failure;
813
814 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
815 if (!dev->wiphy.bands[band])
816 continue;
817
818 nl_band = nla_nest_start(msg, band);
819 if (!nl_band)
820 goto nla_put_failure;
821
Johannes Bergd51626d2008-10-09 12:20:13 +0200822 /* add HT info */
David S. Miller9360ffd2012-03-29 04:41:26 -0400823 if (dev->wiphy.bands[band]->ht_cap.ht_supported &&
824 (nla_put(msg, NL80211_BAND_ATTR_HT_MCS_SET,
825 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
826 &dev->wiphy.bands[band]->ht_cap.mcs) ||
827 nla_put_u16(msg, NL80211_BAND_ATTR_HT_CAPA,
828 dev->wiphy.bands[band]->ht_cap.cap) ||
829 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
830 dev->wiphy.bands[band]->ht_cap.ampdu_factor) ||
831 nla_put_u8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
832 dev->wiphy.bands[band]->ht_cap.ampdu_density)))
833 goto nla_put_failure;
Johannes Bergd51626d2008-10-09 12:20:13 +0200834
Johannes Bergee688b002008-01-24 19:38:39 +0100835 /* add frequencies */
836 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
837 if (!nl_freqs)
838 goto nla_put_failure;
839
840 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
841 nl_freq = nla_nest_start(msg, i);
842 if (!nl_freq)
843 goto nla_put_failure;
844
845 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100846
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400847 if (nl80211_msg_put_channel(msg, chan))
848 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200849
Johannes Bergee688b002008-01-24 19:38:39 +0100850 nla_nest_end(msg, nl_freq);
851 }
852
853 nla_nest_end(msg, nl_freqs);
854
855 /* add bitrates */
856 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
857 if (!nl_rates)
858 goto nla_put_failure;
859
860 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
861 nl_rate = nla_nest_start(msg, i);
862 if (!nl_rate)
863 goto nla_put_failure;
864
865 rate = &dev->wiphy.bands[band]->bitrates[i];
David S. Miller9360ffd2012-03-29 04:41:26 -0400866 if (nla_put_u32(msg, NL80211_BITRATE_ATTR_RATE,
867 rate->bitrate))
868 goto nla_put_failure;
869 if ((rate->flags & IEEE80211_RATE_SHORT_PREAMBLE) &&
870 nla_put_flag(msg,
871 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE))
872 goto nla_put_failure;
Johannes Bergee688b002008-01-24 19:38:39 +0100873
874 nla_nest_end(msg, nl_rate);
875 }
876
877 nla_nest_end(msg, nl_rates);
878
879 nla_nest_end(msg, nl_band);
880 }
881 nla_nest_end(msg, nl_bands);
882
Johannes Berg8fdc6212009-03-14 09:34:01 +0100883 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
884 if (!nl_cmds)
885 goto nla_put_failure;
886
887 i = 0;
888#define CMD(op, n) \
889 do { \
890 if (dev->ops->op) { \
891 i++; \
David S. Miller9360ffd2012-03-29 04:41:26 -0400892 if (nla_put_u32(msg, i, NL80211_CMD_ ## n)) \
893 goto nla_put_failure; \
Johannes Berg8fdc6212009-03-14 09:34:01 +0100894 } \
895 } while (0)
896
897 CMD(add_virtual_intf, NEW_INTERFACE);
898 CMD(change_virtual_intf, SET_INTERFACE);
899 CMD(add_key, NEW_KEY);
Johannes Berg88600202012-02-13 15:17:18 +0100900 CMD(start_ap, START_AP);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100901 CMD(add_station, NEW_STATION);
902 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800903 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100904 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200905 CMD(auth, AUTHENTICATE);
906 CMD(assoc, ASSOCIATE);
907 CMD(deauth, DEAUTHENTICATE);
908 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200909 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100910 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100911 CMD(set_pmksa, SET_PMKSA);
912 CMD(del_pmksa, DEL_PMKSA);
913 CMD(flush_pmksa, FLUSH_PMKSA);
Johannes Berg7c4ef712011-11-18 15:33:48 +0100914 if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
915 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200916 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f782010-08-12 15:38:38 +0200917 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100918 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100919 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200920 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -0400921 if (nla_put_u32(msg, i, NL80211_CMD_SET_WIPHY_NETNS))
922 goto nla_put_failure;
Johannes Berg463d0182009-07-14 00:33:35 +0200923 }
Johannes Berge8c9bd52012-06-06 08:18:22 +0200924 if (dev->ops->set_monitor_channel || dev->ops->start_ap ||
Johannes Bergcc1d2802012-05-16 23:50:20 +0200925 dev->ops->join_mesh) {
Johannes Bergaa430da2012-05-16 23:50:18 +0200926 i++;
927 if (nla_put_u32(msg, i, NL80211_CMD_SET_CHANNEL))
928 goto nla_put_failure;
929 }
Bill Jordane8347eb2010-10-01 13:54:28 -0400930 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300931 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
932 CMD(tdls_mgmt, TDLS_MGMT);
933 CMD(tdls_oper, TDLS_OPER);
934 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300935 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
936 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +0100937 CMD(probe_client, PROBE_CLIENT);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100938 CMD(set_noack_map, SET_NOACK_MAP);
Johannes Berg5e760232011-11-04 11:18:17 +0100939 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
940 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -0400941 if (nla_put_u32(msg, i, NL80211_CMD_REGISTER_BEACONS))
942 goto nla_put_failure;
Johannes Berg5e760232011-11-04 11:18:17 +0100943 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100944
Kalle Valo4745fc02011-11-17 19:06:10 +0200945#ifdef CONFIG_NL80211_TESTMODE
946 CMD(testmode_cmd, TESTMODE);
947#endif
948
Johannes Berg8fdc6212009-03-14 09:34:01 +0100949#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200950
Johannes Berg6829c872009-07-02 09:13:27 +0200951 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200952 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -0400953 if (nla_put_u32(msg, i, NL80211_CMD_CONNECT))
954 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +0200955 }
956
Johannes Berg6829c872009-07-02 09:13:27 +0200957 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200958 i++;
David S. Miller9360ffd2012-03-29 04:41:26 -0400959 if (nla_put_u32(msg, i, NL80211_CMD_DISCONNECT))
960 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +0200961 }
962
Johannes Berg8fdc6212009-03-14 09:34:01 +0100963 nla_nest_end(msg, nl_cmds);
964
Johannes Berg7c4ef712011-11-18 15:33:48 +0100965 if (dev->ops->remain_on_channel &&
David S. Miller9360ffd2012-03-29 04:41:26 -0400966 (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL) &&
967 nla_put_u32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
968 dev->wiphy.max_remain_on_channel_duration))
969 goto nla_put_failure;
Johannes Berga2939112010-12-14 17:54:28 +0100970
David S. Miller9360ffd2012-03-29 04:41:26 -0400971 if ((dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX) &&
972 nla_put_flag(msg, NL80211_ATTR_OFFCHANNEL_TX_OK))
973 goto nla_put_failure;
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100974
Johannes Berg2e161f782010-08-12 15:38:38 +0200975 if (mgmt_stypes) {
976 u16 stypes;
977 struct nlattr *nl_ftypes, *nl_ifs;
978 enum nl80211_iftype ift;
979
980 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
981 if (!nl_ifs)
982 goto nla_put_failure;
983
984 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
985 nl_ftypes = nla_nest_start(msg, ift);
986 if (!nl_ftypes)
987 goto nla_put_failure;
988 i = 0;
989 stypes = mgmt_stypes[ift].tx;
990 while (stypes) {
David S. Miller9360ffd2012-03-29 04:41:26 -0400991 if ((stypes & 1) &&
992 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
993 (i << 4) | IEEE80211_FTYPE_MGMT))
994 goto nla_put_failure;
Johannes Berg2e161f782010-08-12 15:38:38 +0200995 stypes >>= 1;
996 i++;
997 }
998 nla_nest_end(msg, nl_ftypes);
999 }
1000
Johannes Berg74b70a42010-08-24 12:15:53 +02001001 nla_nest_end(msg, nl_ifs);
1002
Johannes Berg2e161f782010-08-12 15:38:38 +02001003 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
1004 if (!nl_ifs)
1005 goto nla_put_failure;
1006
1007 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
1008 nl_ftypes = nla_nest_start(msg, ift);
1009 if (!nl_ftypes)
1010 goto nla_put_failure;
1011 i = 0;
1012 stypes = mgmt_stypes[ift].rx;
1013 while (stypes) {
David S. Miller9360ffd2012-03-29 04:41:26 -04001014 if ((stypes & 1) &&
1015 nla_put_u16(msg, NL80211_ATTR_FRAME_TYPE,
1016 (i << 4) | IEEE80211_FTYPE_MGMT))
1017 goto nla_put_failure;
Johannes Berg2e161f782010-08-12 15:38:38 +02001018 stypes >>= 1;
1019 i++;
1020 }
1021 nla_nest_end(msg, nl_ftypes);
1022 }
1023 nla_nest_end(msg, nl_ifs);
1024 }
1025
Johannes Bergff1b6e62011-05-04 15:37:28 +02001026 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
1027 struct nlattr *nl_wowlan;
1028
1029 nl_wowlan = nla_nest_start(msg,
1030 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
1031 if (!nl_wowlan)
1032 goto nla_put_failure;
1033
David S. Miller9360ffd2012-03-29 04:41:26 -04001034 if (((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY) &&
1035 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
1036 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT) &&
1037 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
1038 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT) &&
1039 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
1040 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY) &&
1041 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED)) ||
1042 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE) &&
1043 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
1044 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ) &&
1045 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
1046 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE) &&
1047 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
1048 ((dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE) &&
1049 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
1050 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001051 if (dev->wiphy.wowlan.n_patterns) {
1052 struct nl80211_wowlan_pattern_support pat = {
1053 .max_patterns = dev->wiphy.wowlan.n_patterns,
1054 .min_pattern_len =
1055 dev->wiphy.wowlan.pattern_min_len,
1056 .max_pattern_len =
1057 dev->wiphy.wowlan.pattern_max_len,
1058 };
David S. Miller9360ffd2012-03-29 04:41:26 -04001059 if (nla_put(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1060 sizeof(pat), &pat))
1061 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02001062 }
1063
1064 nla_nest_end(msg, nl_wowlan);
1065 }
1066
Johannes Berg7527a782011-05-13 10:58:57 +02001067 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1068 dev->wiphy.software_iftypes))
1069 goto nla_put_failure;
1070
1071 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1072 goto nla_put_failure;
1073
David S. Miller9360ffd2012-03-29 04:41:26 -04001074 if ((dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME) &&
1075 nla_put_u32(msg, NL80211_ATTR_DEVICE_AP_SME,
1076 dev->wiphy.ap_sme_capa))
1077 goto nla_put_failure;
Johannes Berg562a7482011-11-07 12:39:33 +01001078
David S. Miller9360ffd2012-03-29 04:41:26 -04001079 if (nla_put_u32(msg, NL80211_ATTR_FEATURE_FLAGS,
1080 dev->wiphy.features))
1081 goto nla_put_failure;
Johannes Berg1f074bd2011-11-06 14:13:33 +01001082
David S. Miller9360ffd2012-03-29 04:41:26 -04001083 if (dev->wiphy.ht_capa_mod_mask &&
1084 nla_put(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
1085 sizeof(*dev->wiphy.ht_capa_mod_mask),
1086 dev->wiphy.ht_capa_mod_mask))
1087 goto nla_put_failure;
Ben Greear7e7c8922011-11-18 11:31:59 -08001088
Johannes Berg55682962007-09-20 13:09:35 -04001089 return genlmsg_end(msg, hdr);
1090
1091 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001092 genlmsg_cancel(msg, hdr);
1093 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001094}
1095
1096static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1097{
1098 int idx = 0;
1099 int start = cb->args[0];
1100 struct cfg80211_registered_device *dev;
1101
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001102 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001103 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001104 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1105 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001106 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001107 continue;
1108 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
1109 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001110 dev) < 0) {
1111 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001112 break;
Julius Volzb4637272008-07-08 14:02:19 +02001113 }
Johannes Berg55682962007-09-20 13:09:35 -04001114 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001115 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001116
1117 cb->args[0] = idx;
1118
1119 return skb->len;
1120}
1121
1122static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1123{
1124 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001125 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001126
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001127 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001128 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001129 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001130
Johannes Berg4c476992010-10-04 21:36:35 +02001131 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1132 nlmsg_free(msg);
1133 return -ENOBUFS;
1134 }
Johannes Berg55682962007-09-20 13:09:35 -04001135
Johannes Berg134e6372009-07-10 09:51:34 +00001136 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001137}
1138
Jouni Malinen31888482008-10-30 16:59:24 +02001139static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1140 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1141 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1142 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1143 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1144 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1145};
1146
1147static int parse_txq_params(struct nlattr *tb[],
1148 struct ieee80211_txq_params *txq_params)
1149{
Johannes Berga3304b02012-03-28 11:04:24 +02001150 if (!tb[NL80211_TXQ_ATTR_AC] || !tb[NL80211_TXQ_ATTR_TXOP] ||
Jouni Malinen31888482008-10-30 16:59:24 +02001151 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1152 !tb[NL80211_TXQ_ATTR_AIFS])
1153 return -EINVAL;
1154
Johannes Berga3304b02012-03-28 11:04:24 +02001155 txq_params->ac = nla_get_u8(tb[NL80211_TXQ_ATTR_AC]);
Jouni Malinen31888482008-10-30 16:59:24 +02001156 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1157 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1158 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1159 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1160
Johannes Berga3304b02012-03-28 11:04:24 +02001161 if (txq_params->ac >= NL80211_NUM_ACS)
1162 return -EINVAL;
1163
Jouni Malinen31888482008-10-30 16:59:24 +02001164 return 0;
1165}
1166
Johannes Bergf444de02010-05-05 15:25:02 +02001167static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1168{
1169 /*
Johannes Bergcc1d2802012-05-16 23:50:20 +02001170 * You can only set the channel explicitly for WDS interfaces,
1171 * all others have their channel managed via their respective
1172 * "establish a connection" command (connect, join, ...)
1173 *
1174 * For AP/GO and mesh mode, the channel can be set with the
1175 * channel userspace API, but is only stored and passed to the
1176 * low-level driver when the AP starts or the mesh is joined.
1177 * This is for backward compatibility, userspace can also give
1178 * the channel in the start-ap or join-mesh commands instead.
Johannes Bergf444de02010-05-05 15:25:02 +02001179 *
1180 * Monitors are special as they are normally slaved to
Johannes Berge8c9bd52012-06-06 08:18:22 +02001181 * whatever else is going on, so they have their own special
1182 * operation to set the monitor channel if possible.
Johannes Bergf444de02010-05-05 15:25:02 +02001183 */
1184 return !wdev ||
1185 wdev->iftype == NL80211_IFTYPE_AP ||
Johannes Bergf444de02010-05-05 15:25:02 +02001186 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001187 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1188 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001189}
1190
Johannes Bergcd6c65982012-05-10 21:27:18 +02001191static bool nl80211_valid_channel_type(struct genl_info *info,
1192 enum nl80211_channel_type *channel_type)
1193{
1194 enum nl80211_channel_type tmp;
1195
1196 if (!info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE])
1197 return false;
1198
1199 tmp = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1200 if (tmp != NL80211_CHAN_NO_HT &&
1201 tmp != NL80211_CHAN_HT20 &&
1202 tmp != NL80211_CHAN_HT40PLUS &&
1203 tmp != NL80211_CHAN_HT40MINUS)
1204 return false;
1205
1206 if (channel_type)
1207 *channel_type = tmp;
1208
1209 return true;
1210}
1211
Johannes Bergf444de02010-05-05 15:25:02 +02001212static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1213 struct wireless_dev *wdev,
1214 struct genl_info *info)
1215{
Johannes Bergaa430da2012-05-16 23:50:18 +02001216 struct ieee80211_channel *channel;
Johannes Bergf444de02010-05-05 15:25:02 +02001217 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1218 u32 freq;
1219 int result;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001220 enum nl80211_iftype iftype = NL80211_IFTYPE_MONITOR;
1221
1222 if (wdev)
1223 iftype = wdev->iftype;
Johannes Bergf444de02010-05-05 15:25:02 +02001224
1225 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1226 return -EINVAL;
1227
1228 if (!nl80211_can_set_dev_channel(wdev))
1229 return -EOPNOTSUPP;
1230
Johannes Bergcd6c65982012-05-10 21:27:18 +02001231 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
1232 !nl80211_valid_channel_type(info, &channel_type))
1233 return -EINVAL;
Johannes Bergf444de02010-05-05 15:25:02 +02001234
1235 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1236
1237 mutex_lock(&rdev->devlist_mtx);
Johannes Berge8c9bd52012-06-06 08:18:22 +02001238 switch (iftype) {
Johannes Bergaa430da2012-05-16 23:50:18 +02001239 case NL80211_IFTYPE_AP:
1240 case NL80211_IFTYPE_P2P_GO:
1241 if (wdev->beacon_interval) {
1242 result = -EBUSY;
1243 break;
1244 }
1245 channel = rdev_freq_to_chan(rdev, freq, channel_type);
1246 if (!channel || !cfg80211_can_beacon_sec_chan(&rdev->wiphy,
1247 channel,
1248 channel_type)) {
1249 result = -EINVAL;
1250 break;
1251 }
1252 wdev->preset_chan = channel;
1253 wdev->preset_chantype = channel_type;
1254 result = 0;
1255 break;
Johannes Bergcc1d2802012-05-16 23:50:20 +02001256 case NL80211_IFTYPE_MESH_POINT:
1257 result = cfg80211_set_mesh_freq(rdev, wdev, freq, channel_type);
1258 break;
Johannes Berge8c9bd52012-06-06 08:18:22 +02001259 case NL80211_IFTYPE_MONITOR:
1260 result = cfg80211_set_monitor_channel(rdev, freq, channel_type);
1261 break;
Johannes Bergaa430da2012-05-16 23:50:18 +02001262 default:
Johannes Berge8c9bd52012-06-06 08:18:22 +02001263 result = -EINVAL;
Johannes Bergf444de02010-05-05 15:25:02 +02001264 }
1265 mutex_unlock(&rdev->devlist_mtx);
1266
1267 return result;
1268}
1269
1270static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1271{
Johannes Berg4c476992010-10-04 21:36:35 +02001272 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1273 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001274
Johannes Berg4c476992010-10-04 21:36:35 +02001275 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001276}
1277
Bill Jordane8347eb2010-10-01 13:54:28 -04001278static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1279{
Johannes Berg43b19952010-10-07 13:10:30 +02001280 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1281 struct net_device *dev = info->user_ptr[1];
1282 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001283 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001284
1285 if (!info->attrs[NL80211_ATTR_MAC])
1286 return -EINVAL;
1287
Johannes Berg43b19952010-10-07 13:10:30 +02001288 if (netif_running(dev))
1289 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001290
Johannes Berg43b19952010-10-07 13:10:30 +02001291 if (!rdev->ops->set_wds_peer)
1292 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001293
Johannes Berg43b19952010-10-07 13:10:30 +02001294 if (wdev->iftype != NL80211_IFTYPE_WDS)
1295 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001296
1297 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001298 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001299}
1300
1301
Johannes Berg55682962007-09-20 13:09:35 -04001302static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1303{
1304 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001305 struct net_device *netdev = NULL;
1306 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001307 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001308 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001309 u32 changed;
1310 u8 retry_short = 0, retry_long = 0;
1311 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001312 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001313
Johannes Bergf444de02010-05-05 15:25:02 +02001314 /*
1315 * Try to find the wiphy and netdev. Normally this
1316 * function shouldn't need the netdev, but this is
1317 * done for backward compatibility -- previously
1318 * setting the channel was done per wiphy, but now
1319 * it is per netdev. Previous userland like hostapd
1320 * also passed a netdev to set_wiphy, so that it is
1321 * possible to let that go to the right netdev!
1322 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001323 mutex_lock(&cfg80211_mutex);
1324
Johannes Bergf444de02010-05-05 15:25:02 +02001325 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1326 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1327
1328 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1329 if (netdev && netdev->ieee80211_ptr) {
1330 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1331 mutex_lock(&rdev->mtx);
1332 } else
1333 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001334 }
1335
Johannes Bergf444de02010-05-05 15:25:02 +02001336 if (!netdev) {
1337 rdev = __cfg80211_rdev_from_info(info);
1338 if (IS_ERR(rdev)) {
1339 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001340 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001341 }
1342 wdev = NULL;
1343 netdev = NULL;
1344 result = 0;
1345
1346 mutex_lock(&rdev->mtx);
Johannes Bergcc1d2802012-05-16 23:50:20 +02001347 } else if (nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
Johannes Bergf444de02010-05-05 15:25:02 +02001348 wdev = netdev->ieee80211_ptr;
1349 else
1350 wdev = NULL;
1351
1352 /*
1353 * end workaround code, by now the rdev is available
1354 * and locked, and wdev may or may not be NULL.
1355 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001356
1357 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001358 result = cfg80211_dev_rename(
1359 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001360
1361 mutex_unlock(&cfg80211_mutex);
1362
1363 if (result)
1364 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001365
Jouni Malinen31888482008-10-30 16:59:24 +02001366 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1367 struct ieee80211_txq_params txq_params;
1368 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1369
1370 if (!rdev->ops->set_txq_params) {
1371 result = -EOPNOTSUPP;
1372 goto bad_res;
1373 }
1374
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001375 if (!netdev) {
1376 result = -EINVAL;
1377 goto bad_res;
1378 }
1379
Johannes Berg133a3ff2011-11-03 14:50:13 +01001380 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1381 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1382 result = -EINVAL;
1383 goto bad_res;
1384 }
1385
Johannes Berg2b5f8b02012-04-02 10:51:55 +02001386 if (!netif_running(netdev)) {
1387 result = -ENETDOWN;
1388 goto bad_res;
1389 }
1390
Jouni Malinen31888482008-10-30 16:59:24 +02001391 nla_for_each_nested(nl_txq_params,
1392 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1393 rem_txq_params) {
1394 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1395 nla_data(nl_txq_params),
1396 nla_len(nl_txq_params),
1397 txq_params_policy);
1398 result = parse_txq_params(tb, &txq_params);
1399 if (result)
1400 goto bad_res;
1401
1402 result = rdev->ops->set_txq_params(&rdev->wiphy,
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001403 netdev,
Jouni Malinen31888482008-10-30 16:59:24 +02001404 &txq_params);
1405 if (result)
1406 goto bad_res;
1407 }
1408 }
1409
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001410 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001411 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001412 if (result)
1413 goto bad_res;
1414 }
1415
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001416 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1417 enum nl80211_tx_power_setting type;
1418 int idx, mbm = 0;
1419
1420 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001421 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001422 goto bad_res;
1423 }
1424
1425 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1426 type = nla_get_u32(info->attrs[idx]);
1427
1428 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1429 (type != NL80211_TX_POWER_AUTOMATIC)) {
1430 result = -EINVAL;
1431 goto bad_res;
1432 }
1433
1434 if (type != NL80211_TX_POWER_AUTOMATIC) {
1435 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1436 mbm = nla_get_u32(info->attrs[idx]);
1437 }
1438
1439 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1440 if (result)
1441 goto bad_res;
1442 }
1443
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001444 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1445 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1446 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001447 if ((!rdev->wiphy.available_antennas_tx &&
1448 !rdev->wiphy.available_antennas_rx) ||
1449 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001450 result = -EOPNOTSUPP;
1451 goto bad_res;
1452 }
1453
1454 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1455 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1456
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001457 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001458 * available antenna masks, except for the "all" mask */
1459 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1460 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001461 result = -EINVAL;
1462 goto bad_res;
1463 }
1464
Bruno Randolf7f531e02010-12-16 11:30:22 +09001465 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1466 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001467
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001468 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1469 if (result)
1470 goto bad_res;
1471 }
1472
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001473 changed = 0;
1474
1475 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1476 retry_short = nla_get_u8(
1477 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1478 if (retry_short == 0) {
1479 result = -EINVAL;
1480 goto bad_res;
1481 }
1482 changed |= WIPHY_PARAM_RETRY_SHORT;
1483 }
1484
1485 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1486 retry_long = nla_get_u8(
1487 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1488 if (retry_long == 0) {
1489 result = -EINVAL;
1490 goto bad_res;
1491 }
1492 changed |= WIPHY_PARAM_RETRY_LONG;
1493 }
1494
1495 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1496 frag_threshold = nla_get_u32(
1497 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1498 if (frag_threshold < 256) {
1499 result = -EINVAL;
1500 goto bad_res;
1501 }
1502 if (frag_threshold != (u32) -1) {
1503 /*
1504 * Fragments (apart from the last one) are required to
1505 * have even length. Make the fragmentation code
1506 * simpler by stripping LSB should someone try to use
1507 * odd threshold value.
1508 */
1509 frag_threshold &= ~0x1;
1510 }
1511 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1512 }
1513
1514 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1515 rts_threshold = nla_get_u32(
1516 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1517 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1518 }
1519
Lukáš Turek81077e82009-12-21 22:50:47 +01001520 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1521 coverage_class = nla_get_u8(
1522 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1523 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1524 }
1525
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001526 if (changed) {
1527 u8 old_retry_short, old_retry_long;
1528 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001529 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001530
1531 if (!rdev->ops->set_wiphy_params) {
1532 result = -EOPNOTSUPP;
1533 goto bad_res;
1534 }
1535
1536 old_retry_short = rdev->wiphy.retry_short;
1537 old_retry_long = rdev->wiphy.retry_long;
1538 old_frag_threshold = rdev->wiphy.frag_threshold;
1539 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001540 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001541
1542 if (changed & WIPHY_PARAM_RETRY_SHORT)
1543 rdev->wiphy.retry_short = retry_short;
1544 if (changed & WIPHY_PARAM_RETRY_LONG)
1545 rdev->wiphy.retry_long = retry_long;
1546 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1547 rdev->wiphy.frag_threshold = frag_threshold;
1548 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1549 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001550 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1551 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001552
1553 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1554 if (result) {
1555 rdev->wiphy.retry_short = old_retry_short;
1556 rdev->wiphy.retry_long = old_retry_long;
1557 rdev->wiphy.frag_threshold = old_frag_threshold;
1558 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001559 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001560 }
1561 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001562
Johannes Berg306d6112008-12-08 12:39:04 +01001563 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001564 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001565 if (netdev)
1566 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001567 return result;
1568}
1569
1570
1571static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001572 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001573 struct net_device *dev)
1574{
1575 void *hdr;
1576
1577 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1578 if (!hdr)
1579 return -1;
1580
David S. Miller9360ffd2012-03-29 04:41:26 -04001581 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
1582 nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
1583 nla_put_string(msg, NL80211_ATTR_IFNAME, dev->name) ||
1584 nla_put_u32(msg, NL80211_ATTR_IFTYPE,
1585 dev->ieee80211_ptr->iftype) ||
1586 nla_put_u32(msg, NL80211_ATTR_GENERATION,
1587 rdev->devlist_generation ^
1588 (cfg80211_rdev_list_generation << 2)))
1589 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02001590
Pontus Fuchsd91df0e2012-04-03 16:39:58 +02001591 if (rdev->ops->get_channel) {
1592 struct ieee80211_channel *chan;
1593 enum nl80211_channel_type channel_type;
1594
1595 chan = rdev->ops->get_channel(&rdev->wiphy, &channel_type);
John W. Linville59ef43e2012-04-18 14:17:13 -04001596 if (chan &&
1597 (nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ,
1598 chan->center_freq) ||
1599 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE,
1600 channel_type)))
1601 goto nla_put_failure;
Pontus Fuchsd91df0e2012-04-03 16:39:58 +02001602 }
1603
Johannes Berg55682962007-09-20 13:09:35 -04001604 return genlmsg_end(msg, hdr);
1605
1606 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001607 genlmsg_cancel(msg, hdr);
1608 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001609}
1610
1611static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1612{
1613 int wp_idx = 0;
1614 int if_idx = 0;
1615 int wp_start = cb->args[0];
1616 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001617 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001618 struct wireless_dev *wdev;
1619
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001620 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001621 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1622 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001623 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001624 if (wp_idx < wp_start) {
1625 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001626 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001627 }
Johannes Berg55682962007-09-20 13:09:35 -04001628 if_idx = 0;
1629
Johannes Bergf5ea9122009-08-07 16:17:38 +02001630 mutex_lock(&rdev->devlist_mtx);
1631 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001632 if (if_idx < if_start) {
1633 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001634 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001635 }
Johannes Berg55682962007-09-20 13:09:35 -04001636 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1637 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001638 rdev, wdev->netdev) < 0) {
1639 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001640 goto out;
1641 }
1642 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001643 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001644 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001645
1646 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001647 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001648 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001649 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001650
1651 cb->args[0] = wp_idx;
1652 cb->args[1] = if_idx;
1653
1654 return skb->len;
1655}
1656
1657static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1658{
1659 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001660 struct cfg80211_registered_device *dev = info->user_ptr[0];
1661 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001662
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001663 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001664 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001665 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001666
Johannes Bergd7264052009-04-19 16:23:20 +02001667 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001668 dev, netdev) < 0) {
1669 nlmsg_free(msg);
1670 return -ENOBUFS;
1671 }
Johannes Berg55682962007-09-20 13:09:35 -04001672
Johannes Berg134e6372009-07-10 09:51:34 +00001673 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001674}
1675
Michael Wu66f7ac52008-01-31 19:48:22 +01001676static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1677 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1678 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1679 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1680 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1681 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1682};
1683
1684static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1685{
1686 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1687 int flag;
1688
1689 *mntrflags = 0;
1690
1691 if (!nla)
1692 return -EINVAL;
1693
1694 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1695 nla, mntr_flags_policy))
1696 return -EINVAL;
1697
1698 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1699 if (flags[flag])
1700 *mntrflags |= (1<<flag);
1701
1702 return 0;
1703}
1704
Johannes Berg9bc383d2009-11-19 11:55:19 +01001705static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001706 struct net_device *netdev, u8 use_4addr,
1707 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001708{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001709 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001710 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001711 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001712 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001713 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001714
1715 switch (iftype) {
1716 case NL80211_IFTYPE_AP_VLAN:
1717 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1718 return 0;
1719 break;
1720 case NL80211_IFTYPE_STATION:
1721 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1722 return 0;
1723 break;
1724 default:
1725 break;
1726 }
1727
1728 return -EOPNOTSUPP;
1729}
1730
Johannes Berg55682962007-09-20 13:09:35 -04001731static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1732{
Johannes Berg4c476992010-10-04 21:36:35 +02001733 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001734 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001735 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001736 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001737 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001738 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001739 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001740
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001741 memset(&params, 0, sizeof(params));
1742
Johannes Berg04a773a2009-04-19 21:24:32 +02001743 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001744
Johannes Berg723b0382008-09-16 20:22:09 +02001745 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001746 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001747 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001748 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001749 if (ntype > NL80211_IFTYPE_MAX)
1750 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001751 }
1752
Johannes Berg92ffe052008-09-16 20:39:36 +02001753 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001754 struct wireless_dev *wdev = dev->ieee80211_ptr;
1755
Johannes Berg4c476992010-10-04 21:36:35 +02001756 if (ntype != NL80211_IFTYPE_MESH_POINT)
1757 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001758 if (netif_running(dev))
1759 return -EBUSY;
1760
1761 wdev_lock(wdev);
1762 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1763 IEEE80211_MAX_MESH_ID_LEN);
1764 wdev->mesh_id_up_len =
1765 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1766 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1767 wdev->mesh_id_up_len);
1768 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001769 }
1770
Felix Fietkau8b787642009-11-10 18:53:10 +01001771 if (info->attrs[NL80211_ATTR_4ADDR]) {
1772 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1773 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001774 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001775 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001776 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001777 } else {
1778 params.use_4addr = -1;
1779 }
1780
Johannes Berg92ffe052008-09-16 20:39:36 +02001781 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001782 if (ntype != NL80211_IFTYPE_MONITOR)
1783 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001784 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1785 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001786 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001787 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001788
1789 flags = &_flags;
1790 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001791 }
Johannes Berg3b858752009-03-12 09:55:09 +01001792
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001793 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001794 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001795 else
1796 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001797
Johannes Berg9bc383d2009-11-19 11:55:19 +01001798 if (!err && params.use_4addr != -1)
1799 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1800
Johannes Berg55682962007-09-20 13:09:35 -04001801 return err;
1802}
1803
1804static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1805{
Johannes Berg4c476992010-10-04 21:36:35 +02001806 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001807 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001808 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001809 int err;
1810 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001811 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001812
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001813 memset(&params, 0, sizeof(params));
1814
Johannes Berg55682962007-09-20 13:09:35 -04001815 if (!info->attrs[NL80211_ATTR_IFNAME])
1816 return -EINVAL;
1817
1818 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1819 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1820 if (type > NL80211_IFTYPE_MAX)
1821 return -EINVAL;
1822 }
1823
Johannes Berg79c97e92009-07-07 03:56:12 +02001824 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001825 !(rdev->wiphy.interface_modes & (1 << type)))
1826 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001827
Johannes Berg9bc383d2009-11-19 11:55:19 +01001828 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001829 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001830 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001831 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001832 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001833 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001834
Michael Wu66f7ac52008-01-31 19:48:22 +01001835 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1836 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1837 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001838 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001839 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001840 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001841 if (IS_ERR(dev))
1842 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001843
Johannes Berg29cbe682010-12-03 09:20:44 +01001844 if (type == NL80211_IFTYPE_MESH_POINT &&
1845 info->attrs[NL80211_ATTR_MESH_ID]) {
1846 struct wireless_dev *wdev = dev->ieee80211_ptr;
1847
1848 wdev_lock(wdev);
1849 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1850 IEEE80211_MAX_MESH_ID_LEN);
1851 wdev->mesh_id_up_len =
1852 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1853 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1854 wdev->mesh_id_up_len);
1855 wdev_unlock(wdev);
1856 }
1857
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001858 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001859}
1860
1861static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1862{
Johannes Berg4c476992010-10-04 21:36:35 +02001863 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1864 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001865
Johannes Berg4c476992010-10-04 21:36:35 +02001866 if (!rdev->ops->del_virtual_intf)
1867 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001868
Johannes Berg4c476992010-10-04 21:36:35 +02001869 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001870}
1871
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01001872static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
1873{
1874 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1875 struct net_device *dev = info->user_ptr[1];
1876 u16 noack_map;
1877
1878 if (!info->attrs[NL80211_ATTR_NOACK_MAP])
1879 return -EINVAL;
1880
1881 if (!rdev->ops->set_noack_map)
1882 return -EOPNOTSUPP;
1883
1884 noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
1885
1886 return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
1887}
1888
Johannes Berg41ade002007-12-19 02:03:29 +01001889struct get_key_cookie {
1890 struct sk_buff *msg;
1891 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001892 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001893};
1894
1895static void get_key_callback(void *c, struct key_params *params)
1896{
Johannes Bergb9454e82009-07-08 13:29:08 +02001897 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001898 struct get_key_cookie *cookie = c;
1899
David S. Miller9360ffd2012-03-29 04:41:26 -04001900 if ((params->key &&
1901 nla_put(cookie->msg, NL80211_ATTR_KEY_DATA,
1902 params->key_len, params->key)) ||
1903 (params->seq &&
1904 nla_put(cookie->msg, NL80211_ATTR_KEY_SEQ,
1905 params->seq_len, params->seq)) ||
1906 (params->cipher &&
1907 nla_put_u32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1908 params->cipher)))
1909 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01001910
Johannes Bergb9454e82009-07-08 13:29:08 +02001911 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1912 if (!key)
1913 goto nla_put_failure;
1914
David S. Miller9360ffd2012-03-29 04:41:26 -04001915 if ((params->key &&
1916 nla_put(cookie->msg, NL80211_KEY_DATA,
1917 params->key_len, params->key)) ||
1918 (params->seq &&
1919 nla_put(cookie->msg, NL80211_KEY_SEQ,
1920 params->seq_len, params->seq)) ||
1921 (params->cipher &&
1922 nla_put_u32(cookie->msg, NL80211_KEY_CIPHER,
1923 params->cipher)))
1924 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02001925
David S. Miller9360ffd2012-03-29 04:41:26 -04001926 if (nla_put_u8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx))
1927 goto nla_put_failure;
Johannes Bergb9454e82009-07-08 13:29:08 +02001928
1929 nla_nest_end(cookie->msg, key);
1930
Johannes Berg41ade002007-12-19 02:03:29 +01001931 return;
1932 nla_put_failure:
1933 cookie->error = 1;
1934}
1935
1936static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1937{
Johannes Berg4c476992010-10-04 21:36:35 +02001938 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001939 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001940 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001941 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001942 const u8 *mac_addr = NULL;
1943 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001944 struct get_key_cookie cookie = {
1945 .error = 0,
1946 };
1947 void *hdr;
1948 struct sk_buff *msg;
1949
1950 if (info->attrs[NL80211_ATTR_KEY_IDX])
1951 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1952
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001953 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001954 return -EINVAL;
1955
1956 if (info->attrs[NL80211_ATTR_MAC])
1957 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1958
Johannes Berge31b8212010-10-05 19:39:30 +02001959 pairwise = !!mac_addr;
1960 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1961 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1962 if (kt >= NUM_NL80211_KEYTYPES)
1963 return -EINVAL;
1964 if (kt != NL80211_KEYTYPE_GROUP &&
1965 kt != NL80211_KEYTYPE_PAIRWISE)
1966 return -EINVAL;
1967 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1968 }
1969
Johannes Berg4c476992010-10-04 21:36:35 +02001970 if (!rdev->ops->get_key)
1971 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001972
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001973 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001974 if (!msg)
1975 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001976
1977 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1978 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001979 if (IS_ERR(hdr))
1980 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001981
1982 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001983 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001984
David S. Miller9360ffd2012-03-29 04:41:26 -04001985 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
1986 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_idx))
1987 goto nla_put_failure;
1988 if (mac_addr &&
1989 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
1990 goto nla_put_failure;
Johannes Berg41ade002007-12-19 02:03:29 +01001991
Johannes Berge31b8212010-10-05 19:39:30 +02001992 if (pairwise && mac_addr &&
1993 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1994 return -ENOENT;
1995
1996 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1997 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001998
1999 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002000 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01002001
2002 if (cookie.error)
2003 goto nla_put_failure;
2004
2005 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02002006 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01002007
2008 nla_put_failure:
2009 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03002010 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01002011 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01002012 return err;
2013}
2014
2015static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
2016{
Johannes Berg4c476992010-10-04 21:36:35 +02002017 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02002018 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002019 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002020 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002021
Johannes Bergb9454e82009-07-08 13:29:08 +02002022 err = nl80211_parse_key(info, &key);
2023 if (err)
2024 return err;
2025
2026 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01002027 return -EINVAL;
2028
Johannes Bergb9454e82009-07-08 13:29:08 +02002029 /* only support setting default key */
2030 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01002031 return -EINVAL;
2032
Johannes Bergfffd0932009-07-08 14:22:54 +02002033 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002034
2035 if (key.def) {
2036 if (!rdev->ops->set_default_key) {
2037 err = -EOPNOTSUPP;
2038 goto out;
2039 }
2040
2041 err = nl80211_key_allowed(dev->ieee80211_ptr);
2042 if (err)
2043 goto out;
2044
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002045 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
2046 key.def_uni, key.def_multi);
2047
2048 if (err)
2049 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02002050
Johannes Berg3d23e342009-09-29 23:27:28 +02002051#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002052 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02002053#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01002054 } else {
2055 if (key.def_uni || !key.def_multi) {
2056 err = -EINVAL;
2057 goto out;
2058 }
2059
2060 if (!rdev->ops->set_default_mgmt_key) {
2061 err = -EOPNOTSUPP;
2062 goto out;
2063 }
2064
2065 err = nl80211_key_allowed(dev->ieee80211_ptr);
2066 if (err)
2067 goto out;
2068
2069 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
2070 dev, key.idx);
2071 if (err)
2072 goto out;
2073
2074#ifdef CONFIG_CFG80211_WEXT
2075 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
2076#endif
2077 }
2078
2079 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02002080 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002081
Johannes Berg41ade002007-12-19 02:03:29 +01002082 return err;
2083}
2084
2085static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
2086{
Johannes Berg4c476992010-10-04 21:36:35 +02002087 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02002088 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002089 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02002090 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02002091 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01002092
Johannes Bergb9454e82009-07-08 13:29:08 +02002093 err = nl80211_parse_key(info, &key);
2094 if (err)
2095 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002096
Johannes Bergb9454e82009-07-08 13:29:08 +02002097 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01002098 return -EINVAL;
2099
Johannes Berg41ade002007-12-19 02:03:29 +01002100 if (info->attrs[NL80211_ATTR_MAC])
2101 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2102
Johannes Berge31b8212010-10-05 19:39:30 +02002103 if (key.type == -1) {
2104 if (mac_addr)
2105 key.type = NL80211_KEYTYPE_PAIRWISE;
2106 else
2107 key.type = NL80211_KEYTYPE_GROUP;
2108 }
2109
2110 /* for now */
2111 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2112 key.type != NL80211_KEYTYPE_GROUP)
2113 return -EINVAL;
2114
Johannes Berg4c476992010-10-04 21:36:35 +02002115 if (!rdev->ops->add_key)
2116 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002117
Johannes Berge31b8212010-10-05 19:39:30 +02002118 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
2119 key.type == NL80211_KEYTYPE_PAIRWISE,
2120 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02002121 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02002122
2123 wdev_lock(dev->ieee80211_ptr);
2124 err = nl80211_key_allowed(dev->ieee80211_ptr);
2125 if (!err)
2126 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02002127 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02002128 mac_addr, &key.p);
2129 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002130
Johannes Berg41ade002007-12-19 02:03:29 +01002131 return err;
2132}
2133
2134static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
2135{
Johannes Berg4c476992010-10-04 21:36:35 +02002136 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002137 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002138 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002139 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002140 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002141
Johannes Bergb9454e82009-07-08 13:29:08 +02002142 err = nl80211_parse_key(info, &key);
2143 if (err)
2144 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002145
2146 if (info->attrs[NL80211_ATTR_MAC])
2147 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2148
Johannes Berge31b8212010-10-05 19:39:30 +02002149 if (key.type == -1) {
2150 if (mac_addr)
2151 key.type = NL80211_KEYTYPE_PAIRWISE;
2152 else
2153 key.type = NL80211_KEYTYPE_GROUP;
2154 }
2155
2156 /* for now */
2157 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2158 key.type != NL80211_KEYTYPE_GROUP)
2159 return -EINVAL;
2160
Johannes Berg4c476992010-10-04 21:36:35 +02002161 if (!rdev->ops->del_key)
2162 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002163
Johannes Bergfffd0932009-07-08 14:22:54 +02002164 wdev_lock(dev->ieee80211_ptr);
2165 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002166
2167 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2168 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2169 err = -ENOENT;
2170
Johannes Bergfffd0932009-07-08 14:22:54 +02002171 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02002172 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
2173 key.type == NL80211_KEYTYPE_PAIRWISE,
2174 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002175
Johannes Berg3d23e342009-09-29 23:27:28 +02002176#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002177 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002178 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002179 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002180 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002181 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2182 }
2183#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002184 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002185
Johannes Berg41ade002007-12-19 02:03:29 +01002186 return err;
2187}
2188
Johannes Berg88600202012-02-13 15:17:18 +01002189static int nl80211_parse_beacon(struct genl_info *info,
2190 struct cfg80211_beacon_data *bcn)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002191{
Johannes Berg88600202012-02-13 15:17:18 +01002192 bool haveinfo = false;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002193
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002194 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2195 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2196 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2197 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002198 return -EINVAL;
2199
Johannes Berg88600202012-02-13 15:17:18 +01002200 memset(bcn, 0, sizeof(*bcn));
Johannes Berged1b6cc2007-12-19 02:03:32 +01002201
Johannes Berged1b6cc2007-12-19 02:03:32 +01002202 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
Johannes Berg88600202012-02-13 15:17:18 +01002203 bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2204 bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2205 if (!bcn->head_len)
2206 return -EINVAL;
2207 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002208 }
2209
2210 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
Johannes Berg88600202012-02-13 15:17:18 +01002211 bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2212 bcn->tail_len =
Johannes Berged1b6cc2007-12-19 02:03:32 +01002213 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
Johannes Berg88600202012-02-13 15:17:18 +01002214 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002215 }
2216
Johannes Berg4c476992010-10-04 21:36:35 +02002217 if (!haveinfo)
2218 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002219
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002220 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg88600202012-02-13 15:17:18 +01002221 bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2222 bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002223 }
2224
2225 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002226 bcn->proberesp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002227 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002228 bcn->proberesp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002229 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2230 }
2231
2232 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002233 bcn->assocresp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002234 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002235 bcn->assocresp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002236 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2237 }
2238
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002239 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002240 bcn->probe_resp =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002241 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002242 bcn->probe_resp_len =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002243 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2244 }
2245
Johannes Berg88600202012-02-13 15:17:18 +01002246 return 0;
2247}
2248
2249static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
2250{
2251 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2252 struct net_device *dev = info->user_ptr[1];
2253 struct wireless_dev *wdev = dev->ieee80211_ptr;
2254 struct cfg80211_ap_settings params;
2255 int err;
2256
2257 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2258 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2259 return -EOPNOTSUPP;
2260
2261 if (!rdev->ops->start_ap)
2262 return -EOPNOTSUPP;
2263
2264 if (wdev->beacon_interval)
2265 return -EALREADY;
2266
2267 memset(&params, 0, sizeof(params));
2268
2269 /* these are required for START_AP */
2270 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2271 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
2272 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2273 return -EINVAL;
2274
2275 err = nl80211_parse_beacon(info, &params.beacon);
2276 if (err)
2277 return err;
2278
2279 params.beacon_interval =
2280 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2281 params.dtim_period =
2282 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2283
2284 err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
2285 if (err)
2286 return err;
2287
2288 /*
2289 * In theory, some of these attributes should be required here
2290 * but since they were not used when the command was originally
2291 * added, keep them optional for old user space programs to let
2292 * them continue to work with drivers that do not need the
2293 * additional information -- drivers must check!
2294 */
2295 if (info->attrs[NL80211_ATTR_SSID]) {
2296 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2297 params.ssid_len =
2298 nla_len(info->attrs[NL80211_ATTR_SSID]);
2299 if (params.ssid_len == 0 ||
2300 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2301 return -EINVAL;
2302 }
2303
2304 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2305 params.hidden_ssid = nla_get_u32(
2306 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2307 if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
2308 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
2309 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2310 return -EINVAL;
2311 }
2312
2313 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2314
2315 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2316 params.auth_type = nla_get_u32(
2317 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2318 if (!nl80211_valid_auth_type(params.auth_type))
2319 return -EINVAL;
2320 } else
2321 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2322
2323 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2324 NL80211_MAX_NR_CIPHER_SUITES);
2325 if (err)
2326 return err;
2327
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +05302328 if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
2329 if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
2330 return -EOPNOTSUPP;
2331 params.inactivity_timeout = nla_get_u16(
2332 info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
2333 }
2334
Johannes Bergaa430da2012-05-16 23:50:18 +02002335 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
2336 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
2337
2338 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
2339 !nl80211_valid_channel_type(info, &channel_type))
2340 return -EINVAL;
2341
2342 params.channel = rdev_freq_to_chan(rdev,
2343 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
2344 channel_type);
2345 if (!params.channel)
2346 return -EINVAL;
2347 params.channel_type = channel_type;
2348 } else if (wdev->preset_chan) {
2349 params.channel = wdev->preset_chan;
2350 params.channel_type = wdev->preset_chantype;
2351 } else
2352 return -EINVAL;
2353
2354 if (!cfg80211_can_beacon_sec_chan(&rdev->wiphy, params.channel,
2355 params.channel_type))
2356 return -EINVAL;
2357
Johannes Berg88600202012-02-13 15:17:18 +01002358 err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
2359 if (!err)
2360 wdev->beacon_interval = params.beacon_interval;
Johannes Berg56d18932011-05-09 18:41:15 +02002361 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002362}
2363
Johannes Berg88600202012-02-13 15:17:18 +01002364static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
2365{
2366 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2367 struct net_device *dev = info->user_ptr[1];
2368 struct wireless_dev *wdev = dev->ieee80211_ptr;
2369 struct cfg80211_beacon_data params;
2370 int err;
2371
2372 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2373 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2374 return -EOPNOTSUPP;
2375
2376 if (!rdev->ops->change_beacon)
2377 return -EOPNOTSUPP;
2378
2379 if (!wdev->beacon_interval)
2380 return -EINVAL;
2381
2382 err = nl80211_parse_beacon(info, &params);
2383 if (err)
2384 return err;
2385
2386 return rdev->ops->change_beacon(&rdev->wiphy, dev, &params);
2387}
2388
2389static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002390{
Johannes Berg4c476992010-10-04 21:36:35 +02002391 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2392 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002393 struct wireless_dev *wdev = dev->ieee80211_ptr;
2394 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002395
Johannes Berg88600202012-02-13 15:17:18 +01002396 if (!rdev->ops->stop_ap)
Johannes Berg4c476992010-10-04 21:36:35 +02002397 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002398
Johannes Berg074ac8d2010-09-16 14:58:22 +02002399 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002400 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2401 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002402
Johannes Berg88600202012-02-13 15:17:18 +01002403 if (!wdev->beacon_interval)
2404 return -ENOENT;
2405
2406 err = rdev->ops->stop_ap(&rdev->wiphy, dev);
Johannes Berg56d18932011-05-09 18:41:15 +02002407 if (!err)
2408 wdev->beacon_interval = 0;
2409 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002410}
2411
Johannes Berg5727ef12007-12-19 02:03:34 +01002412static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2413 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2414 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2415 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002416 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002417 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Bergd83023d2011-12-14 09:29:15 +01002418 [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002419};
2420
Johannes Bergeccb8e82009-05-11 21:57:56 +03002421static int parse_station_flags(struct genl_info *info,
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002422 enum nl80211_iftype iftype,
Johannes Bergeccb8e82009-05-11 21:57:56 +03002423 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002424{
2425 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002426 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002427 int flag;
2428
Johannes Bergeccb8e82009-05-11 21:57:56 +03002429 /*
2430 * Try parsing the new attribute first so userspace
2431 * can specify both for older kernels.
2432 */
2433 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2434 if (nla) {
2435 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002436
Johannes Bergeccb8e82009-05-11 21:57:56 +03002437 sta_flags = nla_data(nla);
2438 params->sta_flags_mask = sta_flags->mask;
2439 params->sta_flags_set = sta_flags->set;
2440 if ((params->sta_flags_mask |
2441 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2442 return -EINVAL;
2443 return 0;
2444 }
2445
2446 /* if present, parse the old attribute */
2447
2448 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002449 if (!nla)
2450 return 0;
2451
2452 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2453 nla, sta_flags_policy))
2454 return -EINVAL;
2455
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002456 /*
2457 * Only allow certain flags for interface types so that
2458 * other attributes are silently ignored. Remember that
2459 * this is backward compatibility code with old userspace
2460 * and shouldn't be hit in other cases anyway.
2461 */
2462 switch (iftype) {
2463 case NL80211_IFTYPE_AP:
2464 case NL80211_IFTYPE_AP_VLAN:
2465 case NL80211_IFTYPE_P2P_GO:
2466 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2467 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2468 BIT(NL80211_STA_FLAG_WME) |
2469 BIT(NL80211_STA_FLAG_MFP);
2470 break;
2471 case NL80211_IFTYPE_P2P_CLIENT:
2472 case NL80211_IFTYPE_STATION:
2473 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2474 BIT(NL80211_STA_FLAG_TDLS_PEER);
2475 break;
2476 case NL80211_IFTYPE_MESH_POINT:
2477 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2478 BIT(NL80211_STA_FLAG_MFP) |
2479 BIT(NL80211_STA_FLAG_AUTHORIZED);
2480 default:
2481 return -EINVAL;
2482 }
Johannes Berg5727ef12007-12-19 02:03:34 +01002483
Johannes Berg3383b5a2012-05-10 20:14:43 +02002484 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) {
2485 if (flags[flag]) {
Johannes Bergeccb8e82009-05-11 21:57:56 +03002486 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002487
Johannes Berg3383b5a2012-05-10 20:14:43 +02002488 /* no longer support new API additions in old API */
2489 if (flag > NL80211_STA_FLAG_MAX_OLD_API)
2490 return -EINVAL;
2491 }
2492 }
2493
Johannes Berg5727ef12007-12-19 02:03:34 +01002494 return 0;
2495}
2496
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002497static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2498 int attr)
2499{
2500 struct nlattr *rate;
2501 u16 bitrate;
2502
2503 rate = nla_nest_start(msg, attr);
2504 if (!rate)
2505 goto nla_put_failure;
2506
2507 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2508 bitrate = cfg80211_calculate_bitrate(info);
David S. Miller9360ffd2012-03-29 04:41:26 -04002509 if ((bitrate > 0 &&
2510 nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate)) ||
2511 ((info->flags & RATE_INFO_FLAGS_MCS) &&
2512 nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs)) ||
2513 ((info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) &&
2514 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH)) ||
2515 ((info->flags & RATE_INFO_FLAGS_SHORT_GI) &&
2516 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI)))
2517 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002518
2519 nla_nest_end(msg, rate);
2520 return true;
2521
2522nla_put_failure:
2523 return false;
2524}
2525
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002526static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
John W. Linville66266b32012-03-15 13:25:41 -04002527 int flags,
2528 struct cfg80211_registered_device *rdev,
2529 struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002530 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002531{
2532 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002533 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002534
2535 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2536 if (!hdr)
2537 return -1;
2538
David S. Miller9360ffd2012-03-29 04:41:26 -04002539 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
2540 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr) ||
2541 nla_put_u32(msg, NL80211_ATTR_GENERATION, sinfo->generation))
2542 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02002543
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002544 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2545 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002546 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04002547 if ((sinfo->filled & STATION_INFO_CONNECTED_TIME) &&
2548 nla_put_u32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2549 sinfo->connected_time))
2550 goto nla_put_failure;
2551 if ((sinfo->filled & STATION_INFO_INACTIVE_TIME) &&
2552 nla_put_u32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2553 sinfo->inactive_time))
2554 goto nla_put_failure;
2555 if ((sinfo->filled & STATION_INFO_RX_BYTES) &&
2556 nla_put_u32(msg, NL80211_STA_INFO_RX_BYTES,
2557 sinfo->rx_bytes))
2558 goto nla_put_failure;
2559 if ((sinfo->filled & STATION_INFO_TX_BYTES) &&
2560 nla_put_u32(msg, NL80211_STA_INFO_TX_BYTES,
2561 sinfo->tx_bytes))
2562 goto nla_put_failure;
2563 if ((sinfo->filled & STATION_INFO_LLID) &&
2564 nla_put_u16(msg, NL80211_STA_INFO_LLID, sinfo->llid))
2565 goto nla_put_failure;
2566 if ((sinfo->filled & STATION_INFO_PLID) &&
2567 nla_put_u16(msg, NL80211_STA_INFO_PLID, sinfo->plid))
2568 goto nla_put_failure;
2569 if ((sinfo->filled & STATION_INFO_PLINK_STATE) &&
2570 nla_put_u8(msg, NL80211_STA_INFO_PLINK_STATE,
2571 sinfo->plink_state))
2572 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04002573 switch (rdev->wiphy.signal_type) {
2574 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04002575 if ((sinfo->filled & STATION_INFO_SIGNAL) &&
2576 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL,
2577 sinfo->signal))
2578 goto nla_put_failure;
2579 if ((sinfo->filled & STATION_INFO_SIGNAL_AVG) &&
2580 nla_put_u8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2581 sinfo->signal_avg))
2582 goto nla_put_failure;
John W. Linville66266b32012-03-15 13:25:41 -04002583 break;
2584 default:
2585 break;
2586 }
Henning Rogge420e7fa2008-12-11 22:04:19 +01002587 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002588 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2589 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002590 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002591 }
2592 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2593 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2594 NL80211_STA_INFO_RX_BITRATE))
2595 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002596 }
David S. Miller9360ffd2012-03-29 04:41:26 -04002597 if ((sinfo->filled & STATION_INFO_RX_PACKETS) &&
2598 nla_put_u32(msg, NL80211_STA_INFO_RX_PACKETS,
2599 sinfo->rx_packets))
2600 goto nla_put_failure;
2601 if ((sinfo->filled & STATION_INFO_TX_PACKETS) &&
2602 nla_put_u32(msg, NL80211_STA_INFO_TX_PACKETS,
2603 sinfo->tx_packets))
2604 goto nla_put_failure;
2605 if ((sinfo->filled & STATION_INFO_TX_RETRIES) &&
2606 nla_put_u32(msg, NL80211_STA_INFO_TX_RETRIES,
2607 sinfo->tx_retries))
2608 goto nla_put_failure;
2609 if ((sinfo->filled & STATION_INFO_TX_FAILED) &&
2610 nla_put_u32(msg, NL80211_STA_INFO_TX_FAILED,
2611 sinfo->tx_failed))
2612 goto nla_put_failure;
2613 if ((sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT) &&
2614 nla_put_u32(msg, NL80211_STA_INFO_BEACON_LOSS,
2615 sinfo->beacon_loss_count))
2616 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07002617 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2618 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2619 if (!bss_param)
2620 goto nla_put_failure;
2621
David S. Miller9360ffd2012-03-29 04:41:26 -04002622 if (((sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT) &&
2623 nla_put_flag(msg, NL80211_STA_BSS_PARAM_CTS_PROT)) ||
2624 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE) &&
2625 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE)) ||
2626 ((sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME) &&
2627 nla_put_flag(msg, NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME)) ||
2628 nla_put_u8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2629 sinfo->bss_param.dtim_period) ||
2630 nla_put_u16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2631 sinfo->bss_param.beacon_interval))
2632 goto nla_put_failure;
Paul Stewartf4263c92011-03-31 09:25:41 -07002633
2634 nla_nest_end(msg, bss_param);
2635 }
David S. Miller9360ffd2012-03-29 04:41:26 -04002636 if ((sinfo->filled & STATION_INFO_STA_FLAGS) &&
2637 nla_put(msg, NL80211_STA_INFO_STA_FLAGS,
2638 sizeof(struct nl80211_sta_flag_update),
2639 &sinfo->sta_flags))
2640 goto nla_put_failure;
John W. Linville7eab0f62012-04-12 14:25:14 -04002641 if ((sinfo->filled & STATION_INFO_T_OFFSET) &&
2642 nla_put_u64(msg, NL80211_STA_INFO_T_OFFSET,
2643 sinfo->t_offset))
2644 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002645 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002646
David S. Miller9360ffd2012-03-29 04:41:26 -04002647 if ((sinfo->filled & STATION_INFO_ASSOC_REQ_IES) &&
2648 nla_put(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2649 sinfo->assoc_req_ies))
2650 goto nla_put_failure;
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002651
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002652 return genlmsg_end(msg, hdr);
2653
2654 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002655 genlmsg_cancel(msg, hdr);
2656 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002657}
2658
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002659static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002660 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002661{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002662 struct station_info sinfo;
2663 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002664 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002665 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002666 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002667 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002668
Johannes Berg67748892010-10-04 21:14:06 +02002669 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2670 if (err)
2671 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002672
2673 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002674 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002675 goto out_err;
2676 }
2677
Johannes Bergbba95fe2008-07-29 13:22:51 +02002678 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002679 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002680 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2681 mac_addr, &sinfo);
2682 if (err == -ENOENT)
2683 break;
2684 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002685 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002686
2687 if (nl80211_send_station(skb,
2688 NETLINK_CB(cb->skb).pid,
2689 cb->nlh->nlmsg_seq, NLM_F_MULTI,
John W. Linville66266b32012-03-15 13:25:41 -04002690 dev, netdev, mac_addr,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002691 &sinfo) < 0)
2692 goto out;
2693
2694 sta_idx++;
2695 }
2696
2697
2698 out:
2699 cb->args[1] = sta_idx;
2700 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002701 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002702 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002703
2704 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002705}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002706
Johannes Berg5727ef12007-12-19 02:03:34 +01002707static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2708{
Johannes Berg4c476992010-10-04 21:36:35 +02002709 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2710 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002711 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002712 struct sk_buff *msg;
2713 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002714 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002715
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002716 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002717
2718 if (!info->attrs[NL80211_ATTR_MAC])
2719 return -EINVAL;
2720
2721 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2722
Johannes Berg4c476992010-10-04 21:36:35 +02002723 if (!rdev->ops->get_station)
2724 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002725
Johannes Berg79c97e92009-07-07 03:56:12 +02002726 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002727 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002728 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002729
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002730 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002731 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002732 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002733
2734 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
John W. Linville66266b32012-03-15 13:25:41 -04002735 rdev, dev, mac_addr, &sinfo) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02002736 nlmsg_free(msg);
2737 return -ENOBUFS;
2738 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002739
Johannes Berg4c476992010-10-04 21:36:35 +02002740 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002741}
2742
2743/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002744 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002745 */
Johannes Berg80b99892011-11-18 16:23:01 +01002746static struct net_device *get_vlan(struct genl_info *info,
2747 struct cfg80211_registered_device *rdev)
Johannes Berg5727ef12007-12-19 02:03:34 +01002748{
Johannes Berg463d0182009-07-14 00:33:35 +02002749 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg80b99892011-11-18 16:23:01 +01002750 struct net_device *v;
2751 int ret;
Johannes Berg5727ef12007-12-19 02:03:34 +01002752
Johannes Berg80b99892011-11-18 16:23:01 +01002753 if (!vlanattr)
2754 return NULL;
2755
2756 v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
2757 if (!v)
2758 return ERR_PTR(-ENODEV);
2759
2760 if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
2761 ret = -EINVAL;
2762 goto error;
Johannes Berg5727ef12007-12-19 02:03:34 +01002763 }
Johannes Berg80b99892011-11-18 16:23:01 +01002764
2765 if (!netif_running(v)) {
2766 ret = -ENETDOWN;
2767 goto error;
2768 }
2769
2770 return v;
2771 error:
2772 dev_put(v);
2773 return ERR_PTR(ret);
Johannes Berg5727ef12007-12-19 02:03:34 +01002774}
2775
2776static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2777{
Johannes Berg4c476992010-10-04 21:36:35 +02002778 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002779 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002780 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002781 struct station_parameters params;
2782 u8 *mac_addr = NULL;
2783
2784 memset(&params, 0, sizeof(params));
2785
2786 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002787 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002788
2789 if (info->attrs[NL80211_ATTR_STA_AID])
2790 return -EINVAL;
2791
2792 if (!info->attrs[NL80211_ATTR_MAC])
2793 return -EINVAL;
2794
2795 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2796
2797 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2798 params.supported_rates =
2799 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2800 params.supported_rates_len =
2801 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2802 }
2803
2804 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2805 params.listen_interval =
2806 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2807
Jouni Malinen36aedc92008-08-25 11:58:58 +03002808 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2809 params.ht_capa =
2810 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2811
Johannes Bergbdd90d52011-12-14 12:20:27 +01002812 if (!rdev->ops->change_station)
2813 return -EOPNOTSUPP;
2814
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002815 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002816 return -EINVAL;
2817
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002818 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2819 params.plink_action =
2820 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2821
Javier Cardona9c3990a2011-05-03 16:57:11 -07002822 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2823 params.plink_state =
2824 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2825
Johannes Berga97f4422009-06-18 17:23:43 +02002826 switch (dev->ieee80211_ptr->iftype) {
2827 case NL80211_IFTYPE_AP:
2828 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002829 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002830 /* disallow mesh-specific things */
2831 if (params.plink_action)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002832 return -EINVAL;
2833
2834 /* TDLS can't be set, ... */
2835 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
2836 return -EINVAL;
2837 /*
2838 * ... but don't bother the driver with it. This works around
2839 * a hostapd/wpa_supplicant issue -- it always includes the
2840 * TLDS_PEER flag in the mask even for AP mode.
2841 */
2842 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2843
2844 /* accept only the listed bits */
2845 if (params.sta_flags_mask &
2846 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2847 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2848 BIT(NL80211_STA_FLAG_WME) |
2849 BIT(NL80211_STA_FLAG_MFP)))
2850 return -EINVAL;
2851
2852 /* must be last in here for error handling */
2853 params.vlan = get_vlan(info, rdev);
2854 if (IS_ERR(params.vlan))
2855 return PTR_ERR(params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002856 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002857 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002858 case NL80211_IFTYPE_STATION:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002859 /*
2860 * Don't allow userspace to change the TDLS_PEER flag,
2861 * but silently ignore attempts to change it since we
2862 * don't have state here to verify that it doesn't try
2863 * to change the flag.
2864 */
2865 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Antonio Quartulli267335d2012-01-31 20:25:47 +01002866 /* fall through */
2867 case NL80211_IFTYPE_ADHOC:
2868 /* disallow things sta doesn't support */
2869 if (params.plink_action)
2870 return -EINVAL;
2871 if (params.ht_capa)
2872 return -EINVAL;
2873 if (params.listen_interval >= 0)
2874 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002875 /* reject any changes other than AUTHORIZED */
2876 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2877 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002878 break;
2879 case NL80211_IFTYPE_MESH_POINT:
2880 /* disallow things mesh doesn't support */
2881 if (params.vlan)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002882 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002883 if (params.ht_capa)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002884 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002885 if (params.listen_interval >= 0)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002886 return -EINVAL;
2887 /*
2888 * No special handling for TDLS here -- the userspace
2889 * mesh code doesn't have this bug.
2890 */
Javier Cardonab39c48f2011-04-07 15:08:30 -07002891 if (params.sta_flags_mask &
2892 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002893 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002894 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Bergbdd90d52011-12-14 12:20:27 +01002895 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002896 break;
2897 default:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002898 return -EOPNOTSUPP;
Johannes Berg034d6552009-05-27 10:35:29 +02002899 }
2900
Johannes Bergbdd90d52011-12-14 12:20:27 +01002901 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01002902
Johannes Berg79c97e92009-07-07 03:56:12 +02002903 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002904
Johannes Berg5727ef12007-12-19 02:03:34 +01002905 if (params.vlan)
2906 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002907
Johannes Berg5727ef12007-12-19 02:03:34 +01002908 return err;
2909}
2910
Eliad Pellerc75786c2011-08-23 14:37:46 +03002911static struct nla_policy
2912nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
2913 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
2914 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
2915};
2916
Johannes Berg5727ef12007-12-19 02:03:34 +01002917static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2918{
Johannes Berg4c476992010-10-04 21:36:35 +02002919 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002920 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002921 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002922 struct station_parameters params;
2923 u8 *mac_addr = NULL;
2924
2925 memset(&params, 0, sizeof(params));
2926
2927 if (!info->attrs[NL80211_ATTR_MAC])
2928 return -EINVAL;
2929
Johannes Berg5727ef12007-12-19 02:03:34 +01002930 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2931 return -EINVAL;
2932
2933 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2934 return -EINVAL;
2935
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002936 if (!info->attrs[NL80211_ATTR_STA_AID])
2937 return -EINVAL;
2938
Johannes Berg5727ef12007-12-19 02:03:34 +01002939 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2940 params.supported_rates =
2941 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2942 params.supported_rates_len =
2943 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2944 params.listen_interval =
2945 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002946
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002947 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2948 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2949 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002950
Jouni Malinen36aedc92008-08-25 11:58:58 +03002951 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2952 params.ht_capa =
2953 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002954
Javier Cardona96b78df2011-04-07 15:08:33 -07002955 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2956 params.plink_action =
2957 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2958
Johannes Bergbdd90d52011-12-14 12:20:27 +01002959 if (!rdev->ops->add_station)
2960 return -EOPNOTSUPP;
2961
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002962 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002963 return -EINVAL;
2964
Johannes Bergbdd90d52011-12-14 12:20:27 +01002965 switch (dev->ieee80211_ptr->iftype) {
2966 case NL80211_IFTYPE_AP:
2967 case NL80211_IFTYPE_AP_VLAN:
2968 case NL80211_IFTYPE_P2P_GO:
2969 /* parse WME attributes if sta is WME capable */
2970 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
2971 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
2972 info->attrs[NL80211_ATTR_STA_WME]) {
2973 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
2974 struct nlattr *nla;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002975
Johannes Bergbdd90d52011-12-14 12:20:27 +01002976 nla = info->attrs[NL80211_ATTR_STA_WME];
2977 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
2978 nl80211_sta_wme_policy);
2979 if (err)
2980 return err;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002981
Johannes Bergbdd90d52011-12-14 12:20:27 +01002982 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
2983 params.uapsd_queues =
2984 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
2985 if (params.uapsd_queues &
2986 ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
2987 return -EINVAL;
2988
2989 if (tb[NL80211_STA_WME_MAX_SP])
2990 params.max_sp =
2991 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
2992
2993 if (params.max_sp &
2994 ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
2995 return -EINVAL;
2996
2997 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
2998 }
2999 /* TDLS peers cannot be added */
3000 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003001 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003002 /* but don't bother the driver with it */
3003 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Eliad Pellerc75786c2011-08-23 14:37:46 +03003004
Johannes Bergbdd90d52011-12-14 12:20:27 +01003005 /* must be last in here for error handling */
3006 params.vlan = get_vlan(info, rdev);
3007 if (IS_ERR(params.vlan))
3008 return PTR_ERR(params.vlan);
3009 break;
3010 case NL80211_IFTYPE_MESH_POINT:
3011 /* TDLS peers cannot be added */
3012 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003013 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003014 break;
3015 case NL80211_IFTYPE_STATION:
3016 /* Only TDLS peers can be added */
3017 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
3018 return -EINVAL;
3019 /* Can only add if TDLS ... */
3020 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
3021 return -EOPNOTSUPP;
3022 /* ... with external setup is supported */
3023 if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
3024 return -EOPNOTSUPP;
3025 break;
3026 default:
3027 return -EOPNOTSUPP;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003028 }
3029
Johannes Bergbdd90d52011-12-14 12:20:27 +01003030 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01003031
Johannes Berg79c97e92009-07-07 03:56:12 +02003032 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01003033
Johannes Berg5727ef12007-12-19 02:03:34 +01003034 if (params.vlan)
3035 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01003036 return err;
3037}
3038
3039static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
3040{
Johannes Berg4c476992010-10-04 21:36:35 +02003041 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3042 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003043 u8 *mac_addr = NULL;
3044
3045 if (info->attrs[NL80211_ATTR_MAC])
3046 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3047
Johannes Berge80cf852009-05-11 14:43:13 +02003048 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02003049 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02003050 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02003051 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3052 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02003053
Johannes Berg4c476992010-10-04 21:36:35 +02003054 if (!rdev->ops->del_station)
3055 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01003056
Johannes Berg4c476992010-10-04 21:36:35 +02003057 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01003058}
3059
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003060static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
3061 int flags, struct net_device *dev,
3062 u8 *dst, u8 *next_hop,
3063 struct mpath_info *pinfo)
3064{
3065 void *hdr;
3066 struct nlattr *pinfoattr;
3067
3068 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
3069 if (!hdr)
3070 return -1;
3071
David S. Miller9360ffd2012-03-29 04:41:26 -04003072 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3073 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, dst) ||
3074 nla_put(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop) ||
3075 nla_put_u32(msg, NL80211_ATTR_GENERATION, pinfo->generation))
3076 goto nla_put_failure;
Johannes Bergf5ea9122009-08-07 16:17:38 +02003077
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003078 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
3079 if (!pinfoattr)
3080 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003081 if ((pinfo->filled & MPATH_INFO_FRAME_QLEN) &&
3082 nla_put_u32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
3083 pinfo->frame_qlen))
3084 goto nla_put_failure;
3085 if (((pinfo->filled & MPATH_INFO_SN) &&
3086 nla_put_u32(msg, NL80211_MPATH_INFO_SN, pinfo->sn)) ||
3087 ((pinfo->filled & MPATH_INFO_METRIC) &&
3088 nla_put_u32(msg, NL80211_MPATH_INFO_METRIC,
3089 pinfo->metric)) ||
3090 ((pinfo->filled & MPATH_INFO_EXPTIME) &&
3091 nla_put_u32(msg, NL80211_MPATH_INFO_EXPTIME,
3092 pinfo->exptime)) ||
3093 ((pinfo->filled & MPATH_INFO_FLAGS) &&
3094 nla_put_u8(msg, NL80211_MPATH_INFO_FLAGS,
3095 pinfo->flags)) ||
3096 ((pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) &&
3097 nla_put_u32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
3098 pinfo->discovery_timeout)) ||
3099 ((pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) &&
3100 nla_put_u8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
3101 pinfo->discovery_retries)))
3102 goto nla_put_failure;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003103
3104 nla_nest_end(msg, pinfoattr);
3105
3106 return genlmsg_end(msg, hdr);
3107
3108 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07003109 genlmsg_cancel(msg, hdr);
3110 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003111}
3112
3113static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003114 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003115{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003116 struct mpath_info pinfo;
3117 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003118 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003119 u8 dst[ETH_ALEN];
3120 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02003121 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003122 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003123
Johannes Berg67748892010-10-04 21:14:06 +02003124 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3125 if (err)
3126 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003127
3128 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003129 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003130 goto out_err;
3131 }
3132
Jouni Malineneec60b02009-03-20 21:21:19 +02003133 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
3134 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02003135 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02003136 }
3137
Johannes Bergbba95fe2008-07-29 13:22:51 +02003138 while (1) {
3139 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
3140 dst, next_hop, &pinfo);
3141 if (err == -ENOENT)
3142 break;
3143 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003144 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003145
3146 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
3147 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3148 netdev, dst, next_hop,
3149 &pinfo) < 0)
3150 goto out;
3151
3152 path_idx++;
3153 }
3154
3155
3156 out:
3157 cb->args[1] = path_idx;
3158 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003159 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003160 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003161 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003162}
3163
3164static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
3165{
Johannes Berg4c476992010-10-04 21:36:35 +02003166 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003167 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003168 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003169 struct mpath_info pinfo;
3170 struct sk_buff *msg;
3171 u8 *dst = NULL;
3172 u8 next_hop[ETH_ALEN];
3173
3174 memset(&pinfo, 0, sizeof(pinfo));
3175
3176 if (!info->attrs[NL80211_ATTR_MAC])
3177 return -EINVAL;
3178
3179 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3180
Johannes Berg4c476992010-10-04 21:36:35 +02003181 if (!rdev->ops->get_mpath)
3182 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003183
Johannes Berg4c476992010-10-04 21:36:35 +02003184 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3185 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003186
Johannes Berg79c97e92009-07-07 03:56:12 +02003187 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003188 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003189 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003190
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003191 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003192 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003193 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003194
3195 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02003196 dev, dst, next_hop, &pinfo) < 0) {
3197 nlmsg_free(msg);
3198 return -ENOBUFS;
3199 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003200
Johannes Berg4c476992010-10-04 21:36:35 +02003201 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003202}
3203
3204static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
3205{
Johannes Berg4c476992010-10-04 21:36:35 +02003206 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3207 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003208 u8 *dst = NULL;
3209 u8 *next_hop = NULL;
3210
3211 if (!info->attrs[NL80211_ATTR_MAC])
3212 return -EINVAL;
3213
3214 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3215 return -EINVAL;
3216
3217 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3218 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3219
Johannes Berg4c476992010-10-04 21:36:35 +02003220 if (!rdev->ops->change_mpath)
3221 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003222
Johannes Berg4c476992010-10-04 21:36:35 +02003223 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3224 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003225
Johannes Berg4c476992010-10-04 21:36:35 +02003226 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003227}
Johannes Berg4c476992010-10-04 21:36:35 +02003228
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003229static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
3230{
Johannes Berg4c476992010-10-04 21:36:35 +02003231 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3232 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003233 u8 *dst = NULL;
3234 u8 *next_hop = NULL;
3235
3236 if (!info->attrs[NL80211_ATTR_MAC])
3237 return -EINVAL;
3238
3239 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3240 return -EINVAL;
3241
3242 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3243 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3244
Johannes Berg4c476992010-10-04 21:36:35 +02003245 if (!rdev->ops->add_mpath)
3246 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003247
Johannes Berg4c476992010-10-04 21:36:35 +02003248 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3249 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003250
Johannes Berg4c476992010-10-04 21:36:35 +02003251 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003252}
3253
3254static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
3255{
Johannes Berg4c476992010-10-04 21:36:35 +02003256 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3257 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003258 u8 *dst = NULL;
3259
3260 if (info->attrs[NL80211_ATTR_MAC])
3261 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3262
Johannes Berg4c476992010-10-04 21:36:35 +02003263 if (!rdev->ops->del_mpath)
3264 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003265
Johannes Berg4c476992010-10-04 21:36:35 +02003266 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003267}
3268
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003269static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
3270{
Johannes Berg4c476992010-10-04 21:36:35 +02003271 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3272 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003273 struct bss_parameters params;
3274
3275 memset(&params, 0, sizeof(params));
3276 /* default to not changing parameters */
3277 params.use_cts_prot = -1;
3278 params.use_short_preamble = -1;
3279 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003280 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01003281 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003282
3283 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
3284 params.use_cts_prot =
3285 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
3286 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
3287 params.use_short_preamble =
3288 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
3289 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
3290 params.use_short_slot_time =
3291 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02003292 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
3293 params.basic_rates =
3294 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3295 params.basic_rates_len =
3296 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3297 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003298 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
3299 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01003300 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
3301 params.ht_opmode =
3302 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003303
Johannes Berg4c476992010-10-04 21:36:35 +02003304 if (!rdev->ops->change_bss)
3305 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003306
Johannes Berg074ac8d2010-09-16 14:58:22 +02003307 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02003308 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3309 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003310
Johannes Berg4c476992010-10-04 21:36:35 +02003311 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003312}
3313
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003314static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003315 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
3316 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
3317 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
3318 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
3319 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
3320 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
3321};
3322
3323static int parse_reg_rule(struct nlattr *tb[],
3324 struct ieee80211_reg_rule *reg_rule)
3325{
3326 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
3327 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
3328
3329 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
3330 return -EINVAL;
3331 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
3332 return -EINVAL;
3333 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
3334 return -EINVAL;
3335 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
3336 return -EINVAL;
3337 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
3338 return -EINVAL;
3339
3340 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
3341
3342 freq_range->start_freq_khz =
3343 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
3344 freq_range->end_freq_khz =
3345 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
3346 freq_range->max_bandwidth_khz =
3347 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
3348
3349 power_rule->max_eirp =
3350 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
3351
3352 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
3353 power_rule->max_antenna_gain =
3354 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
3355
3356 return 0;
3357}
3358
3359static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3360{
3361 int r;
3362 char *data = NULL;
3363
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003364 /*
3365 * You should only get this when cfg80211 hasn't yet initialized
3366 * completely when built-in to the kernel right between the time
3367 * window between nl80211_init() and regulatory_init(), if that is
3368 * even possible.
3369 */
3370 mutex_lock(&cfg80211_mutex);
3371 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003372 mutex_unlock(&cfg80211_mutex);
3373 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003374 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003375 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003376
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003377 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3378 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003379
3380 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3381
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003382 r = regulatory_hint_user(data);
3383
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003384 return r;
3385}
3386
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003387static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003388 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003389{
Johannes Berg4c476992010-10-04 21:36:35 +02003390 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003391 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003392 struct wireless_dev *wdev = dev->ieee80211_ptr;
3393 struct mesh_config cur_params;
3394 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003395 void *hdr;
3396 struct nlattr *pinfoattr;
3397 struct sk_buff *msg;
3398
Johannes Berg29cbe682010-12-03 09:20:44 +01003399 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3400 return -EOPNOTSUPP;
3401
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003402 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02003403 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02003404
Johannes Berg29cbe682010-12-03 09:20:44 +01003405 wdev_lock(wdev);
3406 /* If not connected, get default parameters */
3407 if (!wdev->mesh_id_len)
3408 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
3409 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003410 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003411 &cur_params);
3412 wdev_unlock(wdev);
3413
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003414 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003415 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003416
3417 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003418 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003419 if (!msg)
3420 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003421 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003422 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003423 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003424 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003425 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003426 if (!pinfoattr)
3427 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04003428 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
3429 nla_put_u16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
3430 cur_params.dot11MeshRetryTimeout) ||
3431 nla_put_u16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3432 cur_params.dot11MeshConfirmTimeout) ||
3433 nla_put_u16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
3434 cur_params.dot11MeshHoldingTimeout) ||
3435 nla_put_u16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
3436 cur_params.dot11MeshMaxPeerLinks) ||
3437 nla_put_u8(msg, NL80211_MESHCONF_MAX_RETRIES,
3438 cur_params.dot11MeshMaxRetries) ||
3439 nla_put_u8(msg, NL80211_MESHCONF_TTL,
3440 cur_params.dot11MeshTTL) ||
3441 nla_put_u8(msg, NL80211_MESHCONF_ELEMENT_TTL,
3442 cur_params.element_ttl) ||
3443 nla_put_u8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3444 cur_params.auto_open_plinks) ||
John W. Linville7eab0f62012-04-12 14:25:14 -04003445 nla_put_u32(msg, NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
3446 cur_params.dot11MeshNbrOffsetMaxNeighbor) ||
David S. Miller9360ffd2012-03-29 04:41:26 -04003447 nla_put_u8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3448 cur_params.dot11MeshHWMPmaxPREQretries) ||
3449 nla_put_u32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3450 cur_params.path_refresh_time) ||
3451 nla_put_u16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3452 cur_params.min_discovery_timeout) ||
3453 nla_put_u32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3454 cur_params.dot11MeshHWMPactivePathTimeout) ||
3455 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3456 cur_params.dot11MeshHWMPpreqMinInterval) ||
3457 nla_put_u16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3458 cur_params.dot11MeshHWMPperrMinInterval) ||
3459 nla_put_u16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3460 cur_params.dot11MeshHWMPnetDiameterTraversalTime) ||
3461 nla_put_u8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3462 cur_params.dot11MeshHWMPRootMode) ||
3463 nla_put_u16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3464 cur_params.dot11MeshHWMPRannInterval) ||
3465 nla_put_u8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3466 cur_params.dot11MeshGateAnnouncementProtocol) ||
3467 nla_put_u8(msg, NL80211_MESHCONF_FORWARDING,
3468 cur_params.dot11MeshForwarding) ||
3469 nla_put_u32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
Ashok Nagarajan70c33ea2012-04-30 14:20:32 -07003470 cur_params.rssi_threshold) ||
3471 nla_put_u32(msg, NL80211_MESHCONF_HT_OPMODE,
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08003472 cur_params.ht_opmode) ||
3473 nla_put_u32(msg, NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
3474 cur_params.dot11MeshHWMPactivePathToRootTimeout) ||
3475 nla_put_u16(msg, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08003476 cur_params.dot11MeshHWMProotInterval) ||
3477 nla_put_u16(msg, NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
3478 cur_params.dot11MeshHWMPconfirmationInterval))
David S. Miller9360ffd2012-03-29 04:41:26 -04003479 goto nla_put_failure;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003480 nla_nest_end(msg, pinfoattr);
3481 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003482 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003483
Johannes Berg3b858752009-03-12 09:55:09 +01003484 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003485 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003486 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003487 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003488 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003489}
3490
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003491static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003492 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3493 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3494 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3495 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3496 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3497 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003498 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003499 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
Javier Cardonad299a1f2012-03-31 11:31:33 -07003500 [NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR] = { .type = NLA_U32 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003501 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3502 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3503 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3504 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3505 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
Thomas Pedersendca7e942011-11-24 17:15:24 -08003506 [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003507 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07003508 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07003509 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07003510 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003511 [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003512 [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32 },
3513 [NL80211_MESHCONF_HT_OPMODE] = { .type = NLA_U16 },
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08003514 [NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT] = { .type = NLA_U32 },
3515 [NL80211_MESHCONF_HWMP_ROOT_INTERVAL] = { .type = NLA_U16 },
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08003516 [NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003517};
3518
Javier Cardonac80d5452010-12-16 17:37:49 -08003519static const struct nla_policy
3520 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
Javier Cardonad299a1f2012-03-31 11:31:33 -07003521 [NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC] = { .type = NLA_U8 },
Javier Cardonac80d5452010-12-16 17:37:49 -08003522 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3523 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003524 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003525 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003526 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003527 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003528};
3529
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003530static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003531 struct mesh_config *cfg,
3532 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003533{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003534 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003535 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003536
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003537#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3538do {\
3539 if (table[attr_num]) {\
3540 cfg->param = nla_fn(table[attr_num]); \
3541 mask |= (1 << (attr_num - 1)); \
3542 } \
3543} while (0);\
3544
3545
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003546 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003547 return -EINVAL;
3548 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003549 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003550 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003551 return -EINVAL;
3552
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003553 /* This makes sure that there aren't more than 32 mesh config
3554 * parameters (otherwise our bitfield scheme would not work.) */
3555 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3556
3557 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003558 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003559 mask, NL80211_MESHCONF_RETRY_TIMEOUT,
3560 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003561 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003562 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3563 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003564 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003565 mask, NL80211_MESHCONF_HOLDING_TIMEOUT,
3566 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003567 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003568 mask, NL80211_MESHCONF_MAX_PEER_LINKS,
3569 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003570 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003571 mask, NL80211_MESHCONF_MAX_RETRIES,
3572 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003573 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003574 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003575 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003576 mask, NL80211_MESHCONF_ELEMENT_TTL,
3577 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003578 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003579 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3580 nla_get_u8);
3581 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshNbrOffsetMaxNeighbor, mask,
3582 NL80211_MESHCONF_SYNC_OFFSET_MAX_NEIGHBOR,
3583 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003584 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003585 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3586 nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003587 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003588 mask, NL80211_MESHCONF_PATH_REFRESH_TIME,
3589 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003590 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003591 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3592 nla_get_u16);
3593 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, mask,
3594 NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3595 nla_get_u32);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003596 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003597 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3598 nla_get_u16);
Thomas Pedersendca7e942011-11-24 17:15:24 -08003599 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003600 mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3601 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003602 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003603 dot11MeshHWMPnetDiameterTraversalTime, mask,
3604 NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3605 nla_get_u16);
3606 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRootMode, mask,
3607 NL80211_MESHCONF_HWMP_ROOTMODE, nla_get_u8);
3608 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPRannInterval, mask,
3609 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3610 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003611 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003612 dot11MeshGateAnnouncementProtocol, mask,
3613 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3614 nla_get_u8);
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003615 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003616 mask, NL80211_MESHCONF_FORWARDING,
3617 nla_get_u8);
Ashok Nagarajan55335132012-02-28 17:04:08 -08003618 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003619 mask, NL80211_MESHCONF_RSSI_THRESHOLD,
3620 nla_get_u32);
Ashok Nagarajan70c33ea2012-04-30 14:20:32 -07003621 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, ht_opmode,
Chun-Yeow Yeoha4f606e2012-06-11 11:59:36 +08003622 mask, NL80211_MESHCONF_HT_OPMODE,
3623 nla_get_u16);
Chun-Yeow Yeohac1073a2012-06-14 02:06:06 +08003624 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathToRootTimeout,
3625 mask,
3626 NL80211_MESHCONF_HWMP_PATH_TO_ROOT_TIMEOUT,
3627 nla_get_u32);
3628 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMProotInterval,
3629 mask, NL80211_MESHCONF_HWMP_ROOT_INTERVAL,
3630 nla_get_u16);
Chun-Yeow Yeoh728b19e2012-06-14 02:06:10 +08003631 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3632 dot11MeshHWMPconfirmationInterval, mask,
3633 NL80211_MESHCONF_HWMP_CONFIRMATION_INTERVAL,
3634 nla_get_u16);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003635 if (mask_out)
3636 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003637
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003638 return 0;
3639
3640#undef FILL_IN_MESH_PARAM_IF_SET
3641}
3642
Javier Cardonac80d5452010-12-16 17:37:49 -08003643static int nl80211_parse_mesh_setup(struct genl_info *info,
3644 struct mesh_setup *setup)
3645{
3646 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3647
3648 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3649 return -EINVAL;
3650 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3651 info->attrs[NL80211_ATTR_MESH_SETUP],
3652 nl80211_mesh_setup_params_policy))
3653 return -EINVAL;
3654
Javier Cardonad299a1f2012-03-31 11:31:33 -07003655 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])
3656 setup->sync_method =
3657 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_SYNC])) ?
3658 IEEE80211_SYNC_METHOD_VENDOR :
3659 IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET;
3660
Javier Cardonac80d5452010-12-16 17:37:49 -08003661 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3662 setup->path_sel_proto =
3663 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3664 IEEE80211_PATH_PROTOCOL_VENDOR :
3665 IEEE80211_PATH_PROTOCOL_HWMP;
3666
3667 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3668 setup->path_metric =
3669 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3670 IEEE80211_PATH_METRIC_VENDOR :
3671 IEEE80211_PATH_METRIC_AIRTIME;
3672
Javier Cardona581a8b02011-04-07 15:08:27 -07003673
3674 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003675 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003676 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003677 if (!is_valid_ie_attr(ieattr))
3678 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003679 setup->ie = nla_data(ieattr);
3680 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003681 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003682 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3683 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003684
3685 return 0;
3686}
3687
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003688static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003689 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003690{
3691 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3692 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003693 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003694 struct mesh_config cfg;
3695 u32 mask;
3696 int err;
3697
Johannes Berg29cbe682010-12-03 09:20:44 +01003698 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3699 return -EOPNOTSUPP;
3700
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003701 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003702 return -EOPNOTSUPP;
3703
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003704 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003705 if (err)
3706 return err;
3707
Johannes Berg29cbe682010-12-03 09:20:44 +01003708 wdev_lock(wdev);
3709 if (!wdev->mesh_id_len)
3710 err = -ENOLINK;
3711
3712 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003713 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003714 mask, &cfg);
3715
3716 wdev_unlock(wdev);
3717
3718 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003719}
3720
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003721static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3722{
3723 struct sk_buff *msg;
3724 void *hdr = NULL;
3725 struct nlattr *nl_reg_rules;
3726 unsigned int i;
3727 int err = -EINVAL;
3728
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003729 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003730
3731 if (!cfg80211_regdomain)
3732 goto out;
3733
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003734 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003735 if (!msg) {
3736 err = -ENOBUFS;
3737 goto out;
3738 }
3739
3740 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3741 NL80211_CMD_GET_REG);
3742 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003743 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003744
David S. Miller9360ffd2012-03-29 04:41:26 -04003745 if (nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
3746 cfg80211_regdomain->alpha2) ||
3747 (cfg80211_regdomain->dfs_region &&
3748 nla_put_u8(msg, NL80211_ATTR_DFS_REGION,
3749 cfg80211_regdomain->dfs_region)))
3750 goto nla_put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003751
3752 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3753 if (!nl_reg_rules)
3754 goto nla_put_failure;
3755
3756 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3757 struct nlattr *nl_reg_rule;
3758 const struct ieee80211_reg_rule *reg_rule;
3759 const struct ieee80211_freq_range *freq_range;
3760 const struct ieee80211_power_rule *power_rule;
3761
3762 reg_rule = &cfg80211_regdomain->reg_rules[i];
3763 freq_range = &reg_rule->freq_range;
3764 power_rule = &reg_rule->power_rule;
3765
3766 nl_reg_rule = nla_nest_start(msg, i);
3767 if (!nl_reg_rule)
3768 goto nla_put_failure;
3769
David S. Miller9360ffd2012-03-29 04:41:26 -04003770 if (nla_put_u32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3771 reg_rule->flags) ||
3772 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_START,
3773 freq_range->start_freq_khz) ||
3774 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_END,
3775 freq_range->end_freq_khz) ||
3776 nla_put_u32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3777 freq_range->max_bandwidth_khz) ||
3778 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3779 power_rule->max_antenna_gain) ||
3780 nla_put_u32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3781 power_rule->max_eirp))
3782 goto nla_put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003783
3784 nla_nest_end(msg, nl_reg_rule);
3785 }
3786
3787 nla_nest_end(msg, nl_reg_rules);
3788
3789 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003790 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003791 goto out;
3792
3793nla_put_failure:
3794 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003795put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003796 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003797 err = -EMSGSIZE;
3798out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003799 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003800 return err;
3801}
3802
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003803static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3804{
3805 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3806 struct nlattr *nl_reg_rule;
3807 char *alpha2 = NULL;
3808 int rem_reg_rules = 0, r = 0;
3809 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003810 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003811 struct ieee80211_regdomain *rd = NULL;
3812
3813 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3814 return -EINVAL;
3815
3816 if (!info->attrs[NL80211_ATTR_REG_RULES])
3817 return -EINVAL;
3818
3819 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3820
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003821 if (info->attrs[NL80211_ATTR_DFS_REGION])
3822 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
3823
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003824 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3825 rem_reg_rules) {
3826 num_rules++;
3827 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003828 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003829 }
3830
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003831 mutex_lock(&cfg80211_mutex);
3832
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003833 if (!reg_is_valid_request(alpha2)) {
3834 r = -EINVAL;
3835 goto bad_reg;
3836 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003837
3838 size_of_regd = sizeof(struct ieee80211_regdomain) +
3839 (num_rules * sizeof(struct ieee80211_reg_rule));
3840
3841 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003842 if (!rd) {
3843 r = -ENOMEM;
3844 goto bad_reg;
3845 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003846
3847 rd->n_reg_rules = num_rules;
3848 rd->alpha2[0] = alpha2[0];
3849 rd->alpha2[1] = alpha2[1];
3850
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003851 /*
3852 * Disable DFS master mode if the DFS region was
3853 * not supported or known on this kernel.
3854 */
3855 if (reg_supported_dfs_region(dfs_region))
3856 rd->dfs_region = dfs_region;
3857
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003858 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3859 rem_reg_rules) {
3860 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3861 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3862 reg_rule_policy);
3863 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3864 if (r)
3865 goto bad_reg;
3866
3867 rule_idx++;
3868
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003869 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3870 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003871 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003872 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003873 }
3874
3875 BUG_ON(rule_idx != num_rules);
3876
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003877 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003878
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003879 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003880
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003881 return r;
3882
Johannes Bergd2372b32008-10-24 20:32:20 +02003883 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003884 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003885 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003886 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003887}
3888
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003889static int validate_scan_freqs(struct nlattr *freqs)
3890{
3891 struct nlattr *attr1, *attr2;
3892 int n_channels = 0, tmp1, tmp2;
3893
3894 nla_for_each_nested(attr1, freqs, tmp1) {
3895 n_channels++;
3896 /*
3897 * Some hardware has a limited channel list for
3898 * scanning, and it is pretty much nonsensical
3899 * to scan for a channel twice, so disallow that
3900 * and don't require drivers to check that the
3901 * channel list they get isn't longer than what
3902 * they can scan, as long as they can scan all
3903 * the channels they registered at once.
3904 */
3905 nla_for_each_nested(attr2, freqs, tmp2)
3906 if (attr1 != attr2 &&
3907 nla_get_u32(attr1) == nla_get_u32(attr2))
3908 return 0;
3909 }
3910
3911 return n_channels;
3912}
3913
Johannes Berg2a519312009-02-10 21:25:55 +01003914static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3915{
Johannes Berg4c476992010-10-04 21:36:35 +02003916 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3917 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003918 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003919 struct nlattr *attr;
3920 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003921 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003922 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003923
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003924 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3925 return -EINVAL;
3926
Johannes Berg79c97e92009-07-07 03:56:12 +02003927 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003928
Johannes Berg4c476992010-10-04 21:36:35 +02003929 if (!rdev->ops->scan)
3930 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003931
Johannes Berg4c476992010-10-04 21:36:35 +02003932 if (rdev->scan_req)
3933 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003934
3935 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003936 n_channels = validate_scan_freqs(
3937 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003938 if (!n_channels)
3939 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003940 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003941 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003942 n_channels = 0;
3943
Johannes Berg2a519312009-02-10 21:25:55 +01003944 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3945 if (wiphy->bands[band])
3946 n_channels += wiphy->bands[band]->n_channels;
3947 }
3948
3949 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3950 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3951 n_ssids++;
3952
Johannes Berg4c476992010-10-04 21:36:35 +02003953 if (n_ssids > wiphy->max_scan_ssids)
3954 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003955
Jouni Malinen70692ad2009-02-16 19:39:13 +02003956 if (info->attrs[NL80211_ATTR_IE])
3957 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3958 else
3959 ie_len = 0;
3960
Johannes Berg4c476992010-10-04 21:36:35 +02003961 if (ie_len > wiphy->max_scan_ie_len)
3962 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003963
Johannes Berg2a519312009-02-10 21:25:55 +01003964 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003965 + sizeof(*request->ssids) * n_ssids
3966 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003967 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003968 if (!request)
3969 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003970
Johannes Berg2a519312009-02-10 21:25:55 +01003971 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003972 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003973 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003974 if (ie_len) {
3975 if (request->ssids)
3976 request->ie = (void *)(request->ssids + n_ssids);
3977 else
3978 request->ie = (void *)(request->channels + n_channels);
3979 }
Johannes Berg2a519312009-02-10 21:25:55 +01003980
Johannes Berg584991d2009-11-02 13:32:03 +01003981 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003982 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3983 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003984 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003985 struct ieee80211_channel *chan;
3986
3987 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3988
3989 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003990 err = -EINVAL;
3991 goto out_free;
3992 }
Johannes Berg584991d2009-11-02 13:32:03 +01003993
3994 /* ignore disabled channels */
3995 if (chan->flags & IEEE80211_CHAN_DISABLED)
3996 continue;
3997
3998 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003999 i++;
4000 }
4001 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02004002 enum ieee80211_band band;
4003
Johannes Berg2a519312009-02-10 21:25:55 +01004004 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01004005 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4006 int j;
4007 if (!wiphy->bands[band])
4008 continue;
4009 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01004010 struct ieee80211_channel *chan;
4011
4012 chan = &wiphy->bands[band]->channels[j];
4013
4014 if (chan->flags & IEEE80211_CHAN_DISABLED)
4015 continue;
4016
4017 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01004018 i++;
4019 }
4020 }
4021 }
4022
Johannes Berg584991d2009-11-02 13:32:03 +01004023 if (!i) {
4024 err = -EINVAL;
4025 goto out_free;
4026 }
4027
4028 request->n_channels = i;
4029
Johannes Berg2a519312009-02-10 21:25:55 +01004030 i = 0;
4031 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4032 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004033 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01004034 err = -EINVAL;
4035 goto out_free;
4036 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004037 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01004038 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01004039 i++;
4040 }
4041 }
4042
Jouni Malinen70692ad2009-02-16 19:39:13 +02004043 if (info->attrs[NL80211_ATTR_IE]) {
4044 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a54b2009-04-01 11:58:36 +02004045 memcpy((void *)request->ie,
4046 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02004047 request->ie_len);
4048 }
4049
Johannes Berg34850ab2011-07-18 18:08:35 +02004050 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02004051 if (wiphy->bands[i])
4052 request->rates[i] =
4053 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02004054
4055 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
4056 nla_for_each_nested(attr,
4057 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
4058 tmp) {
4059 enum ieee80211_band band = nla_type(attr);
4060
Dan Carpenter84404622011-07-29 11:52:18 +03004061 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02004062 err = -EINVAL;
4063 goto out_free;
4064 }
4065 err = ieee80211_get_ratemask(wiphy->bands[band],
4066 nla_data(attr),
4067 nla_len(attr),
4068 &request->rates[band]);
4069 if (err)
4070 goto out_free;
4071 }
4072 }
4073
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05304074 request->no_cck =
4075 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
4076
Johannes Berg463d0182009-07-14 00:33:35 +02004077 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02004078 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01004079
Johannes Berg79c97e92009-07-07 03:56:12 +02004080 rdev->scan_req = request;
4081 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01004082
Johannes Berg463d0182009-07-14 00:33:35 +02004083 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02004084 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02004085 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02004086 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01004087 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02004088 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01004089 kfree(request);
4090 }
Johannes Berg3b858752009-03-12 09:55:09 +01004091
Johannes Berg2a519312009-02-10 21:25:55 +01004092 return err;
4093}
4094
Luciano Coelho807f8a82011-05-11 17:09:35 +03004095static int nl80211_start_sched_scan(struct sk_buff *skb,
4096 struct genl_info *info)
4097{
4098 struct cfg80211_sched_scan_request *request;
4099 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4100 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004101 struct nlattr *attr;
4102 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004103 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004104 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004105 enum ieee80211_band band;
4106 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004107 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004108
4109 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4110 !rdev->ops->sched_scan_start)
4111 return -EOPNOTSUPP;
4112
4113 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4114 return -EINVAL;
4115
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004116 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
4117 return -EINVAL;
4118
4119 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
4120 if (interval == 0)
4121 return -EINVAL;
4122
Luciano Coelho807f8a82011-05-11 17:09:35 +03004123 wiphy = &rdev->wiphy;
4124
4125 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4126 n_channels = validate_scan_freqs(
4127 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
4128 if (!n_channels)
4129 return -EINVAL;
4130 } else {
4131 n_channels = 0;
4132
4133 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
4134 if (wiphy->bands[band])
4135 n_channels += wiphy->bands[band]->n_channels;
4136 }
4137
4138 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
4139 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4140 tmp)
4141 n_ssids++;
4142
Luciano Coelho93b6aa62011-07-13 14:57:28 +03004143 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004144 return -EINVAL;
4145
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004146 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
4147 nla_for_each_nested(attr,
4148 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4149 tmp)
4150 n_match_sets++;
4151
4152 if (n_match_sets > wiphy->max_match_sets)
4153 return -EINVAL;
4154
Luciano Coelho807f8a82011-05-11 17:09:35 +03004155 if (info->attrs[NL80211_ATTR_IE])
4156 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4157 else
4158 ie_len = 0;
4159
Luciano Coelho5a865ba2011-07-13 14:57:29 +03004160 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004161 return -EINVAL;
4162
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004163 mutex_lock(&rdev->sched_scan_mtx);
4164
4165 if (rdev->sched_scan_req) {
4166 err = -EINPROGRESS;
4167 goto out;
4168 }
4169
Luciano Coelho807f8a82011-05-11 17:09:35 +03004170 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004171 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004172 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004173 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03004174 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004175 if (!request) {
4176 err = -ENOMEM;
4177 goto out;
4178 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03004179
4180 if (n_ssids)
4181 request->ssids = (void *)&request->channels[n_channels];
4182 request->n_ssids = n_ssids;
4183 if (ie_len) {
4184 if (request->ssids)
4185 request->ie = (void *)(request->ssids + n_ssids);
4186 else
4187 request->ie = (void *)(request->channels + n_channels);
4188 }
4189
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004190 if (n_match_sets) {
4191 if (request->ie)
4192 request->match_sets = (void *)(request->ie + ie_len);
4193 else if (request->ssids)
4194 request->match_sets =
4195 (void *)(request->ssids + n_ssids);
4196 else
4197 request->match_sets =
4198 (void *)(request->channels + n_channels);
4199 }
4200 request->n_match_sets = n_match_sets;
4201
Luciano Coelho807f8a82011-05-11 17:09:35 +03004202 i = 0;
4203 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4204 /* user specified, bail out if channel not found */
4205 nla_for_each_nested(attr,
4206 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
4207 tmp) {
4208 struct ieee80211_channel *chan;
4209
4210 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
4211
4212 if (!chan) {
4213 err = -EINVAL;
4214 goto out_free;
4215 }
4216
4217 /* ignore disabled channels */
4218 if (chan->flags & IEEE80211_CHAN_DISABLED)
4219 continue;
4220
4221 request->channels[i] = chan;
4222 i++;
4223 }
4224 } else {
4225 /* all channels */
4226 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4227 int j;
4228 if (!wiphy->bands[band])
4229 continue;
4230 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
4231 struct ieee80211_channel *chan;
4232
4233 chan = &wiphy->bands[band]->channels[j];
4234
4235 if (chan->flags & IEEE80211_CHAN_DISABLED)
4236 continue;
4237
4238 request->channels[i] = chan;
4239 i++;
4240 }
4241 }
4242 }
4243
4244 if (!i) {
4245 err = -EINVAL;
4246 goto out_free;
4247 }
4248
4249 request->n_channels = i;
4250
4251 i = 0;
4252 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4253 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4254 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004255 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03004256 err = -EINVAL;
4257 goto out_free;
4258 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004259 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004260 memcpy(request->ssids[i].ssid, nla_data(attr),
4261 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03004262 i++;
4263 }
4264 }
4265
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004266 i = 0;
4267 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
4268 nla_for_each_nested(attr,
4269 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4270 tmp) {
4271 struct nlattr *ssid;
4272
4273 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
4274 nla_data(attr), nla_len(attr),
4275 nl80211_match_policy);
Johannes Berg4a4ab0d2012-06-13 11:17:11 +02004276 ssid = tb[NL80211_SCHED_SCAN_MATCH_ATTR_SSID];
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004277 if (ssid) {
4278 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
4279 err = -EINVAL;
4280 goto out_free;
4281 }
4282 memcpy(request->match_sets[i].ssid.ssid,
4283 nla_data(ssid), nla_len(ssid));
4284 request->match_sets[i].ssid.ssid_len =
4285 nla_len(ssid);
4286 }
4287 i++;
4288 }
4289 }
4290
Luciano Coelho807f8a82011-05-11 17:09:35 +03004291 if (info->attrs[NL80211_ATTR_IE]) {
4292 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4293 memcpy((void *)request->ie,
4294 nla_data(info->attrs[NL80211_ATTR_IE]),
4295 request->ie_len);
4296 }
4297
4298 request->dev = dev;
4299 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004300 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004301
4302 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
4303 if (!err) {
4304 rdev->sched_scan_req = request;
4305 nl80211_send_sched_scan(rdev, dev,
4306 NL80211_CMD_START_SCHED_SCAN);
4307 goto out;
4308 }
4309
4310out_free:
4311 kfree(request);
4312out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004313 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004314 return err;
4315}
4316
4317static int nl80211_stop_sched_scan(struct sk_buff *skb,
4318 struct genl_info *info)
4319{
4320 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004321 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004322
4323 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4324 !rdev->ops->sched_scan_stop)
4325 return -EOPNOTSUPP;
4326
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004327 mutex_lock(&rdev->sched_scan_mtx);
4328 err = __cfg80211_stop_sched_scan(rdev, false);
4329 mutex_unlock(&rdev->sched_scan_mtx);
4330
4331 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004332}
4333
Johannes Berg9720bb32011-06-21 09:45:33 +02004334static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
4335 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004336 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02004337 struct wireless_dev *wdev,
4338 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01004339{
Johannes Berg48ab9052009-07-10 18:42:31 +02004340 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01004341 void *hdr;
4342 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02004343
4344 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004345
Johannes Berg9720bb32011-06-21 09:45:33 +02004346 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004347 NL80211_CMD_NEW_SCAN_RESULTS);
4348 if (!hdr)
4349 return -1;
4350
Johannes Berg9720bb32011-06-21 09:45:33 +02004351 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
4352
David S. Miller9360ffd2012-03-29 04:41:26 -04004353 if (nla_put_u32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation) ||
4354 nla_put_u32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex))
4355 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004356
4357 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
4358 if (!bss)
4359 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04004360 if ((!is_zero_ether_addr(res->bssid) &&
4361 nla_put(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid)) ||
4362 (res->information_elements && res->len_information_elements &&
4363 nla_put(msg, NL80211_BSS_INFORMATION_ELEMENTS,
4364 res->len_information_elements,
4365 res->information_elements)) ||
4366 (res->beacon_ies && res->len_beacon_ies &&
4367 res->beacon_ies != res->information_elements &&
4368 nla_put(msg, NL80211_BSS_BEACON_IES,
4369 res->len_beacon_ies, res->beacon_ies)))
4370 goto nla_put_failure;
4371 if (res->tsf &&
4372 nla_put_u64(msg, NL80211_BSS_TSF, res->tsf))
4373 goto nla_put_failure;
4374 if (res->beacon_interval &&
4375 nla_put_u16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval))
4376 goto nla_put_failure;
4377 if (nla_put_u16(msg, NL80211_BSS_CAPABILITY, res->capability) ||
4378 nla_put_u32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq) ||
4379 nla_put_u32(msg, NL80211_BSS_SEEN_MS_AGO,
4380 jiffies_to_msecs(jiffies - intbss->ts)))
4381 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004382
Johannes Berg77965c972009-02-18 18:45:06 +01004383 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01004384 case CFG80211_SIGNAL_TYPE_MBM:
David S. Miller9360ffd2012-03-29 04:41:26 -04004385 if (nla_put_u32(msg, NL80211_BSS_SIGNAL_MBM, res->signal))
4386 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004387 break;
4388 case CFG80211_SIGNAL_TYPE_UNSPEC:
David S. Miller9360ffd2012-03-29 04:41:26 -04004389 if (nla_put_u8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal))
4390 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01004391 break;
4392 default:
4393 break;
4394 }
4395
Johannes Berg48ab9052009-07-10 18:42:31 +02004396 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02004397 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02004398 case NL80211_IFTYPE_STATION:
David S. Miller9360ffd2012-03-29 04:41:26 -04004399 if (intbss == wdev->current_bss &&
4400 nla_put_u32(msg, NL80211_BSS_STATUS,
4401 NL80211_BSS_STATUS_ASSOCIATED))
4402 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02004403 break;
4404 case NL80211_IFTYPE_ADHOC:
David S. Miller9360ffd2012-03-29 04:41:26 -04004405 if (intbss == wdev->current_bss &&
4406 nla_put_u32(msg, NL80211_BSS_STATUS,
4407 NL80211_BSS_STATUS_IBSS_JOINED))
4408 goto nla_put_failure;
Johannes Berg48ab9052009-07-10 18:42:31 +02004409 break;
4410 default:
4411 break;
4412 }
4413
Johannes Berg2a519312009-02-10 21:25:55 +01004414 nla_nest_end(msg, bss);
4415
4416 return genlmsg_end(msg, hdr);
4417
4418 nla_put_failure:
4419 genlmsg_cancel(msg, hdr);
4420 return -EMSGSIZE;
4421}
4422
4423static int nl80211_dump_scan(struct sk_buff *skb,
4424 struct netlink_callback *cb)
4425{
Johannes Berg48ab9052009-07-10 18:42:31 +02004426 struct cfg80211_registered_device *rdev;
4427 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01004428 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02004429 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01004430 int start = cb->args[1], idx = 0;
4431 int err;
4432
Johannes Berg67748892010-10-04 21:14:06 +02004433 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
4434 if (err)
4435 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01004436
Johannes Berg48ab9052009-07-10 18:42:31 +02004437 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01004438
Johannes Berg48ab9052009-07-10 18:42:31 +02004439 wdev_lock(wdev);
4440 spin_lock_bh(&rdev->bss_lock);
4441 cfg80211_bss_expire(rdev);
4442
Johannes Berg9720bb32011-06-21 09:45:33 +02004443 cb->seq = rdev->bss_generation;
4444
Johannes Berg48ab9052009-07-10 18:42:31 +02004445 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01004446 if (++idx <= start)
4447 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02004448 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01004449 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02004450 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004451 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02004452 break;
Johannes Berg2a519312009-02-10 21:25:55 +01004453 }
4454 }
4455
Johannes Berg48ab9052009-07-10 18:42:31 +02004456 spin_unlock_bh(&rdev->bss_lock);
4457 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004458
4459 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02004460 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004461
Johannes Berg67748892010-10-04 21:14:06 +02004462 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01004463}
4464
Holger Schurig61fa7132009-11-11 12:25:40 +01004465static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
4466 int flags, struct net_device *dev,
4467 struct survey_info *survey)
4468{
4469 void *hdr;
4470 struct nlattr *infoattr;
4471
Holger Schurig61fa7132009-11-11 12:25:40 +01004472 hdr = nl80211hdr_put(msg, pid, seq, flags,
4473 NL80211_CMD_NEW_SURVEY_RESULTS);
4474 if (!hdr)
4475 return -ENOMEM;
4476
David S. Miller9360ffd2012-03-29 04:41:26 -04004477 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex))
4478 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01004479
4480 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
4481 if (!infoattr)
4482 goto nla_put_failure;
4483
David S. Miller9360ffd2012-03-29 04:41:26 -04004484 if (nla_put_u32(msg, NL80211_SURVEY_INFO_FREQUENCY,
4485 survey->channel->center_freq))
4486 goto nla_put_failure;
4487
4488 if ((survey->filled & SURVEY_INFO_NOISE_DBM) &&
4489 nla_put_u8(msg, NL80211_SURVEY_INFO_NOISE, survey->noise))
4490 goto nla_put_failure;
4491 if ((survey->filled & SURVEY_INFO_IN_USE) &&
4492 nla_put_flag(msg, NL80211_SURVEY_INFO_IN_USE))
4493 goto nla_put_failure;
4494 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME) &&
4495 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
4496 survey->channel_time))
4497 goto nla_put_failure;
4498 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY) &&
4499 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
4500 survey->channel_time_busy))
4501 goto nla_put_failure;
4502 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY) &&
4503 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
4504 survey->channel_time_ext_busy))
4505 goto nla_put_failure;
4506 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_RX) &&
4507 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
4508 survey->channel_time_rx))
4509 goto nla_put_failure;
4510 if ((survey->filled & SURVEY_INFO_CHANNEL_TIME_TX) &&
4511 nla_put_u64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
4512 survey->channel_time_tx))
4513 goto nla_put_failure;
Holger Schurig61fa7132009-11-11 12:25:40 +01004514
4515 nla_nest_end(msg, infoattr);
4516
4517 return genlmsg_end(msg, hdr);
4518
4519 nla_put_failure:
4520 genlmsg_cancel(msg, hdr);
4521 return -EMSGSIZE;
4522}
4523
4524static int nl80211_dump_survey(struct sk_buff *skb,
4525 struct netlink_callback *cb)
4526{
4527 struct survey_info survey;
4528 struct cfg80211_registered_device *dev;
4529 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01004530 int survey_idx = cb->args[1];
4531 int res;
4532
Johannes Berg67748892010-10-04 21:14:06 +02004533 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
4534 if (res)
4535 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01004536
4537 if (!dev->ops->dump_survey) {
4538 res = -EOPNOTSUPP;
4539 goto out_err;
4540 }
4541
4542 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004543 struct ieee80211_channel *chan;
4544
Holger Schurig61fa7132009-11-11 12:25:40 +01004545 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
4546 &survey);
4547 if (res == -ENOENT)
4548 break;
4549 if (res)
4550 goto out_err;
4551
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004552 /* Survey without a channel doesn't make sense */
4553 if (!survey.channel) {
4554 res = -EINVAL;
4555 goto out;
4556 }
4557
4558 chan = ieee80211_get_channel(&dev->wiphy,
4559 survey.channel->center_freq);
4560 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
4561 survey_idx++;
4562 continue;
4563 }
4564
Holger Schurig61fa7132009-11-11 12:25:40 +01004565 if (nl80211_send_survey(skb,
4566 NETLINK_CB(cb->skb).pid,
4567 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4568 netdev,
4569 &survey) < 0)
4570 goto out;
4571 survey_idx++;
4572 }
4573
4574 out:
4575 cb->args[1] = survey_idx;
4576 res = skb->len;
4577 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02004578 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01004579 return res;
4580}
4581
Jouni Malinen255e7372009-03-20 21:21:17 +02004582static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
4583{
Samuel Ortizb23aa672009-07-01 21:26:54 +02004584 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02004585}
4586
Samuel Ortizb23aa672009-07-01 21:26:54 +02004587static bool nl80211_valid_wpa_versions(u32 wpa_versions)
4588{
4589 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
4590 NL80211_WPA_VERSION_2));
4591}
4592
Jouni Malinen636a5d32009-03-19 13:39:22 +02004593static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
4594{
Johannes Berg4c476992010-10-04 21:36:35 +02004595 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4596 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004597 struct ieee80211_channel *chan;
4598 const u8 *bssid, *ssid, *ie = NULL;
4599 int err, ssid_len, ie_len = 0;
4600 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02004601 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004602 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004603
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004604 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4605 return -EINVAL;
4606
4607 if (!info->attrs[NL80211_ATTR_MAC])
4608 return -EINVAL;
4609
Jouni Malinen17780922009-03-27 20:52:47 +02004610 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4611 return -EINVAL;
4612
Johannes Berg19957bb2009-07-02 17:20:43 +02004613 if (!info->attrs[NL80211_ATTR_SSID])
4614 return -EINVAL;
4615
4616 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4617 return -EINVAL;
4618
Johannes Bergfffd0932009-07-08 14:22:54 +02004619 err = nl80211_parse_key(info, &key);
4620 if (err)
4621 return err;
4622
4623 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004624 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4625 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004626 if (!key.p.key || !key.p.key_len)
4627 return -EINVAL;
4628 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4629 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4630 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4631 key.p.key_len != WLAN_KEY_LEN_WEP104))
4632 return -EINVAL;
4633 if (key.idx > 4)
4634 return -EINVAL;
4635 } else {
4636 key.p.key_len = 0;
4637 key.p.key = NULL;
4638 }
4639
Johannes Bergafea0b72010-08-10 09:46:42 +02004640 if (key.idx >= 0) {
4641 int i;
4642 bool ok = false;
4643 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4644 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4645 ok = true;
4646 break;
4647 }
4648 }
Johannes Berg4c476992010-10-04 21:36:35 +02004649 if (!ok)
4650 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004651 }
4652
Johannes Berg4c476992010-10-04 21:36:35 +02004653 if (!rdev->ops->auth)
4654 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004655
Johannes Berg074ac8d2010-09-16 14:58:22 +02004656 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004657 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4658 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004659
Johannes Berg19957bb2009-07-02 17:20:43 +02004660 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004661 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004662 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004663 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4664 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004665
Johannes Berg19957bb2009-07-02 17:20:43 +02004666 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4667 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4668
4669 if (info->attrs[NL80211_ATTR_IE]) {
4670 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4671 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4672 }
4673
4674 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004675 if (!nl80211_valid_auth_type(auth_type))
4676 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004677
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004678 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4679
Johannes Berg95de8172012-01-20 13:55:25 +01004680 /*
4681 * Since we no longer track auth state, ignore
4682 * requests to only change local state.
4683 */
4684 if (local_state_change)
4685 return 0;
4686
Johannes Berg4c476992010-10-04 21:36:35 +02004687 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4688 ssid, ssid_len, ie, ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +01004689 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004690}
4691
Johannes Bergc0692b82010-08-27 14:26:53 +03004692static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4693 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004694 struct cfg80211_crypto_settings *settings,
4695 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004696{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004697 memset(settings, 0, sizeof(*settings));
4698
Samuel Ortizb23aa672009-07-01 21:26:54 +02004699 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4700
Johannes Bergc0692b82010-08-27 14:26:53 +03004701 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4702 u16 proto;
4703 proto = nla_get_u16(
4704 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4705 settings->control_port_ethertype = cpu_to_be16(proto);
4706 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4707 proto != ETH_P_PAE)
4708 return -EINVAL;
4709 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4710 settings->control_port_no_encrypt = true;
4711 } else
4712 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4713
Samuel Ortizb23aa672009-07-01 21:26:54 +02004714 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4715 void *data;
4716 int len, i;
4717
4718 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4719 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4720 settings->n_ciphers_pairwise = len / sizeof(u32);
4721
4722 if (len % sizeof(u32))
4723 return -EINVAL;
4724
Johannes Berg3dc27d22009-07-02 21:36:37 +02004725 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004726 return -EINVAL;
4727
4728 memcpy(settings->ciphers_pairwise, data, len);
4729
4730 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004731 if (!cfg80211_supported_cipher_suite(
4732 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004733 settings->ciphers_pairwise[i]))
4734 return -EINVAL;
4735 }
4736
4737 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4738 settings->cipher_group =
4739 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004740 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
4741 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02004742 return -EINVAL;
4743 }
4744
4745 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4746 settings->wpa_versions =
4747 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4748 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4749 return -EINVAL;
4750 }
4751
4752 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4753 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03004754 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004755
4756 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4757 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4758 settings->n_akm_suites = len / sizeof(u32);
4759
4760 if (len % sizeof(u32))
4761 return -EINVAL;
4762
Jouni Malinen1b9ca022011-09-21 16:13:07 +03004763 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4764 return -EINVAL;
4765
Samuel Ortizb23aa672009-07-01 21:26:54 +02004766 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004767 }
4768
4769 return 0;
4770}
4771
Jouni Malinen636a5d32009-03-19 13:39:22 +02004772static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4773{
Johannes Berg4c476992010-10-04 21:36:35 +02004774 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4775 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004776 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004777 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004778 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004779 int err, ssid_len, ie_len = 0;
4780 bool use_mfp = false;
Ben Greear7e7c8922011-11-18 11:31:59 -08004781 u32 flags = 0;
4782 struct ieee80211_ht_cap *ht_capa = NULL;
4783 struct ieee80211_ht_cap *ht_capa_mask = NULL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004784
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004785 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4786 return -EINVAL;
4787
4788 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004789 !info->attrs[NL80211_ATTR_SSID] ||
4790 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004791 return -EINVAL;
4792
Johannes Berg4c476992010-10-04 21:36:35 +02004793 if (!rdev->ops->assoc)
4794 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004795
Johannes Berg074ac8d2010-09-16 14:58:22 +02004796 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004797 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4798 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004799
Johannes Berg19957bb2009-07-02 17:20:43 +02004800 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004801
Johannes Berg19957bb2009-07-02 17:20:43 +02004802 chan = ieee80211_get_channel(&rdev->wiphy,
4803 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004804 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4805 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004806
Johannes Berg19957bb2009-07-02 17:20:43 +02004807 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4808 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004809
4810 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004811 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4812 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004813 }
4814
Jouni Malinendc6382ce2009-05-06 22:09:37 +03004815 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004816 enum nl80211_mfp mfp =
Jouni Malinendc6382ce2009-05-06 22:09:37 +03004817 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004818 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004819 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004820 else if (mfp != NL80211_MFP_NO)
4821 return -EINVAL;
Jouni Malinendc6382ce2009-05-06 22:09:37 +03004822 }
4823
Johannes Berg3e5d7642009-07-07 14:37:26 +02004824 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4825 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4826
Ben Greear7e7c8922011-11-18 11:31:59 -08004827 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
4828 flags |= ASSOC_REQ_DISABLE_HT;
4829
4830 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
4831 ht_capa_mask =
4832 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
4833
4834 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
4835 if (!ht_capa_mask)
4836 return -EINVAL;
4837 ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
4838 }
4839
Johannes Bergc0692b82010-08-27 14:26:53 +03004840 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004841 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004842 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4843 ssid, ssid_len, ie, ie_len, use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -08004844 &crypto, flags, ht_capa,
4845 ht_capa_mask);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004846
Jouni Malinen636a5d32009-03-19 13:39:22 +02004847 return err;
4848}
4849
4850static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4851{
Johannes Berg4c476992010-10-04 21:36:35 +02004852 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4853 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004854 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004855 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004856 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004857 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004858
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004859 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4860 return -EINVAL;
4861
4862 if (!info->attrs[NL80211_ATTR_MAC])
4863 return -EINVAL;
4864
4865 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4866 return -EINVAL;
4867
Johannes Berg4c476992010-10-04 21:36:35 +02004868 if (!rdev->ops->deauth)
4869 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004870
Johannes Berg074ac8d2010-09-16 14:58:22 +02004871 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004872 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4873 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004874
Johannes Berg19957bb2009-07-02 17:20:43 +02004875 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004876
Johannes Berg19957bb2009-07-02 17:20:43 +02004877 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4878 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004879 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004880 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004881 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004882
4883 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004884 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4885 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004886 }
4887
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004888 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4889
Johannes Berg4c476992010-10-04 21:36:35 +02004890 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4891 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004892}
4893
4894static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4895{
Johannes Berg4c476992010-10-04 21:36:35 +02004896 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4897 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004898 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004899 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004900 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004901 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004902
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004903 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4904 return -EINVAL;
4905
4906 if (!info->attrs[NL80211_ATTR_MAC])
4907 return -EINVAL;
4908
4909 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4910 return -EINVAL;
4911
Johannes Berg4c476992010-10-04 21:36:35 +02004912 if (!rdev->ops->disassoc)
4913 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004914
Johannes Berg074ac8d2010-09-16 14:58:22 +02004915 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004916 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4917 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004918
Johannes Berg19957bb2009-07-02 17:20:43 +02004919 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004920
Johannes Berg19957bb2009-07-02 17:20:43 +02004921 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4922 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004923 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004924 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004925 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004926
4927 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004928 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4929 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004930 }
4931
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004932 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4933
Johannes Berg4c476992010-10-04 21:36:35 +02004934 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4935 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004936}
4937
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004938static bool
4939nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4940 int mcast_rate[IEEE80211_NUM_BANDS],
4941 int rateval)
4942{
4943 struct wiphy *wiphy = &rdev->wiphy;
4944 bool found = false;
4945 int band, i;
4946
4947 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4948 struct ieee80211_supported_band *sband;
4949
4950 sband = wiphy->bands[band];
4951 if (!sband)
4952 continue;
4953
4954 for (i = 0; i < sband->n_bitrates; i++) {
4955 if (sband->bitrates[i].bitrate == rateval) {
4956 mcast_rate[band] = i + 1;
4957 found = true;
4958 break;
4959 }
4960 }
4961 }
4962
4963 return found;
4964}
4965
Johannes Berg04a773a2009-04-19 21:24:32 +02004966static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4967{
Johannes Berg4c476992010-10-04 21:36:35 +02004968 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4969 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004970 struct cfg80211_ibss_params ibss;
4971 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004972 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004973 int err;
4974
Johannes Berg8e30bc52009-04-22 17:45:38 +02004975 memset(&ibss, 0, sizeof(ibss));
4976
Johannes Berg04a773a2009-04-19 21:24:32 +02004977 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4978 return -EINVAL;
4979
4980 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4981 !info->attrs[NL80211_ATTR_SSID] ||
4982 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4983 return -EINVAL;
4984
Johannes Berg8e30bc52009-04-22 17:45:38 +02004985 ibss.beacon_interval = 100;
4986
4987 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4988 ibss.beacon_interval =
4989 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4990 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4991 return -EINVAL;
4992 }
4993
Johannes Berg4c476992010-10-04 21:36:35 +02004994 if (!rdev->ops->join_ibss)
4995 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004996
Johannes Berg4c476992010-10-04 21:36:35 +02004997 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4998 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004999
Johannes Berg79c97e92009-07-07 03:56:12 +02005000 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02005001
Johannes Berg39193492011-09-16 13:45:25 +02005002 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02005003 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02005004
5005 if (!is_valid_ether_addr(ibss.bssid))
5006 return -EINVAL;
5007 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005008 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5009 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5010
5011 if (info->attrs[NL80211_ATTR_IE]) {
5012 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5013 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5014 }
5015
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005016 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5017 enum nl80211_channel_type channel_type;
5018
Johannes Bergcd6c65982012-05-10 21:27:18 +02005019 if (!nl80211_valid_channel_type(info, &channel_type))
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005020 return -EINVAL;
5021
5022 if (channel_type != NL80211_CHAN_NO_HT &&
5023 !(wiphy->features & NL80211_FEATURE_HT_IBSS))
5024 return -EINVAL;
5025
5026 ibss.channel_type = channel_type;
5027 } else {
5028 ibss.channel_type = NL80211_CHAN_NO_HT;
5029 }
5030
5031 ibss.channel = rdev_freq_to_chan(rdev,
5032 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
5033 ibss.channel_type);
Johannes Berg04a773a2009-04-19 21:24:32 +02005034 if (!ibss.channel ||
5035 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02005036 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
5037 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02005038
Alexander Simon54858ee5b2011-11-30 16:56:32 +01005039 /* Both channels should be able to initiate communication */
5040 if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
5041 ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
5042 !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
5043 ibss.channel_type))
5044 return -EINVAL;
5045
Johannes Berg04a773a2009-04-19 21:24:32 +02005046 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02005047 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02005048
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005049 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
5050 u8 *rates =
5051 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5052 int n_rates =
5053 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5054 struct ieee80211_supported_band *sband =
5055 wiphy->bands[ibss.channel->band];
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005056
Johannes Berg34850ab2011-07-18 18:08:35 +02005057 err = ieee80211_get_ratemask(sband, rates, n_rates,
5058 &ibss.basic_rates);
5059 if (err)
5060 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005061 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01005062
5063 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
5064 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
5065 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
5066 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005067
Johannes Berg4c476992010-10-04 21:36:35 +02005068 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5069 connkeys = nl80211_parse_connkeys(rdev,
5070 info->attrs[NL80211_ATTR_KEYS]);
5071 if (IS_ERR(connkeys))
5072 return PTR_ERR(connkeys);
5073 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005074
Antonio Quartulli267335d2012-01-31 20:25:47 +01005075 ibss.control_port =
5076 nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
5077
Johannes Berg4c476992010-10-04 21:36:35 +02005078 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005079 if (err)
5080 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02005081 return err;
5082}
5083
5084static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
5085{
Johannes Berg4c476992010-10-04 21:36:35 +02005086 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5087 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02005088
Johannes Berg4c476992010-10-04 21:36:35 +02005089 if (!rdev->ops->leave_ibss)
5090 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005091
Johannes Berg4c476992010-10-04 21:36:35 +02005092 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
5093 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005094
Johannes Berg4c476992010-10-04 21:36:35 +02005095 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02005096}
5097
Johannes Bergaff89a92009-07-01 21:26:51 +02005098#ifdef CONFIG_NL80211_TESTMODE
5099static struct genl_multicast_group nl80211_testmode_mcgrp = {
5100 .name = "testmode",
5101};
5102
5103static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
5104{
Johannes Berg4c476992010-10-04 21:36:35 +02005105 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02005106 int err;
5107
5108 if (!info->attrs[NL80211_ATTR_TESTDATA])
5109 return -EINVAL;
5110
Johannes Bergaff89a92009-07-01 21:26:51 +02005111 err = -EOPNOTSUPP;
5112 if (rdev->ops->testmode_cmd) {
5113 rdev->testmode_info = info;
5114 err = rdev->ops->testmode_cmd(&rdev->wiphy,
5115 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
5116 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
5117 rdev->testmode_info = NULL;
5118 }
5119
Johannes Bergaff89a92009-07-01 21:26:51 +02005120 return err;
5121}
5122
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005123static int nl80211_testmode_dump(struct sk_buff *skb,
5124 struct netlink_callback *cb)
5125{
Johannes Berg00918d32011-12-13 17:22:05 +01005126 struct cfg80211_registered_device *rdev;
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005127 int err;
5128 long phy_idx;
5129 void *data = NULL;
5130 int data_len = 0;
5131
5132 if (cb->args[0]) {
5133 /*
5134 * 0 is a valid index, but not valid for args[0],
5135 * so we need to offset by 1.
5136 */
5137 phy_idx = cb->args[0] - 1;
5138 } else {
5139 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
5140 nl80211_fam.attrbuf, nl80211_fam.maxattr,
5141 nl80211_policy);
5142 if (err)
5143 return err;
Johannes Berg00918d32011-12-13 17:22:05 +01005144 if (nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) {
5145 phy_idx = nla_get_u32(
5146 nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
5147 } else {
5148 struct net_device *netdev;
5149
5150 err = get_rdev_dev_by_ifindex(sock_net(skb->sk),
5151 nl80211_fam.attrbuf,
5152 &rdev, &netdev);
5153 if (err)
5154 return err;
5155 dev_put(netdev);
5156 phy_idx = rdev->wiphy_idx;
5157 cfg80211_unlock_rdev(rdev);
5158 }
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005159 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
5160 cb->args[1] =
5161 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
5162 }
5163
5164 if (cb->args[1]) {
5165 data = nla_data((void *)cb->args[1]);
5166 data_len = nla_len((void *)cb->args[1]);
5167 }
5168
5169 mutex_lock(&cfg80211_mutex);
Johannes Berg00918d32011-12-13 17:22:05 +01005170 rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
5171 if (!rdev) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005172 mutex_unlock(&cfg80211_mutex);
5173 return -ENOENT;
5174 }
Johannes Berg00918d32011-12-13 17:22:05 +01005175 cfg80211_lock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005176 mutex_unlock(&cfg80211_mutex);
5177
Johannes Berg00918d32011-12-13 17:22:05 +01005178 if (!rdev->ops->testmode_dump) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005179 err = -EOPNOTSUPP;
5180 goto out_err;
5181 }
5182
5183 while (1) {
5184 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
5185 cb->nlh->nlmsg_seq, NLM_F_MULTI,
5186 NL80211_CMD_TESTMODE);
5187 struct nlattr *tmdata;
5188
David S. Miller9360ffd2012-03-29 04:41:26 -04005189 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005190 genlmsg_cancel(skb, hdr);
5191 break;
5192 }
5193
5194 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5195 if (!tmdata) {
5196 genlmsg_cancel(skb, hdr);
5197 break;
5198 }
Johannes Berg00918d32011-12-13 17:22:05 +01005199 err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb,
5200 data, data_len);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005201 nla_nest_end(skb, tmdata);
5202
5203 if (err == -ENOBUFS || err == -ENOENT) {
5204 genlmsg_cancel(skb, hdr);
5205 break;
5206 } else if (err) {
5207 genlmsg_cancel(skb, hdr);
5208 goto out_err;
5209 }
5210
5211 genlmsg_end(skb, hdr);
5212 }
5213
5214 err = skb->len;
5215 /* see above */
5216 cb->args[0] = phy_idx + 1;
5217 out_err:
Johannes Berg00918d32011-12-13 17:22:05 +01005218 cfg80211_unlock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005219 return err;
5220}
5221
Johannes Bergaff89a92009-07-01 21:26:51 +02005222static struct sk_buff *
5223__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
5224 int approxlen, u32 pid, u32 seq, gfp_t gfp)
5225{
5226 struct sk_buff *skb;
5227 void *hdr;
5228 struct nlattr *data;
5229
5230 skb = nlmsg_new(approxlen + 100, gfp);
5231 if (!skb)
5232 return NULL;
5233
5234 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
5235 if (!hdr) {
5236 kfree_skb(skb);
5237 return NULL;
5238 }
5239
David S. Miller9360ffd2012-03-29 04:41:26 -04005240 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx))
5241 goto nla_put_failure;
Johannes Bergaff89a92009-07-01 21:26:51 +02005242 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5243
5244 ((void **)skb->cb)[0] = rdev;
5245 ((void **)skb->cb)[1] = hdr;
5246 ((void **)skb->cb)[2] = data;
5247
5248 return skb;
5249
5250 nla_put_failure:
5251 kfree_skb(skb);
5252 return NULL;
5253}
5254
5255struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
5256 int approxlen)
5257{
5258 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5259
5260 if (WARN_ON(!rdev->testmode_info))
5261 return NULL;
5262
5263 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
5264 rdev->testmode_info->snd_pid,
5265 rdev->testmode_info->snd_seq,
5266 GFP_KERNEL);
5267}
5268EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
5269
5270int cfg80211_testmode_reply(struct sk_buff *skb)
5271{
5272 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
5273 void *hdr = ((void **)skb->cb)[1];
5274 struct nlattr *data = ((void **)skb->cb)[2];
5275
5276 if (WARN_ON(!rdev->testmode_info)) {
5277 kfree_skb(skb);
5278 return -EINVAL;
5279 }
5280
5281 nla_nest_end(skb, data);
5282 genlmsg_end(skb, hdr);
5283 return genlmsg_reply(skb, rdev->testmode_info);
5284}
5285EXPORT_SYMBOL(cfg80211_testmode_reply);
5286
5287struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
5288 int approxlen, gfp_t gfp)
5289{
5290 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5291
5292 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
5293}
5294EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
5295
5296void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
5297{
5298 void *hdr = ((void **)skb->cb)[1];
5299 struct nlattr *data = ((void **)skb->cb)[2];
5300
5301 nla_nest_end(skb, data);
5302 genlmsg_end(skb, hdr);
5303 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
5304}
5305EXPORT_SYMBOL(cfg80211_testmode_event);
5306#endif
5307
Samuel Ortizb23aa672009-07-01 21:26:54 +02005308static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
5309{
Johannes Berg4c476992010-10-04 21:36:35 +02005310 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5311 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005312 struct cfg80211_connect_params connect;
5313 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02005314 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005315 int err;
5316
5317 memset(&connect, 0, sizeof(connect));
5318
5319 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5320 return -EINVAL;
5321
5322 if (!info->attrs[NL80211_ATTR_SSID] ||
5323 !nla_len(info->attrs[NL80211_ATTR_SSID]))
5324 return -EINVAL;
5325
5326 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
5327 connect.auth_type =
5328 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
5329 if (!nl80211_valid_auth_type(connect.auth_type))
5330 return -EINVAL;
5331 } else
5332 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
5333
5334 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
5335
Johannes Bergc0692b82010-08-27 14:26:53 +03005336 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02005337 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005338 if (err)
5339 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005340
Johannes Berg074ac8d2010-09-16 14:58:22 +02005341 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005342 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5343 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005344
Johannes Berg79c97e92009-07-07 03:56:12 +02005345 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005346
Bala Shanmugam4486ea92012-03-07 17:27:12 +05305347 connect.bg_scan_period = -1;
5348 if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
5349 (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
5350 connect.bg_scan_period =
5351 nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
5352 }
5353
Samuel Ortizb23aa672009-07-01 21:26:54 +02005354 if (info->attrs[NL80211_ATTR_MAC])
5355 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5356 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5357 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5358
5359 if (info->attrs[NL80211_ATTR_IE]) {
5360 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5361 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5362 }
5363
5364 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
5365 connect.channel =
5366 ieee80211_get_channel(wiphy,
5367 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
5368 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02005369 connect.channel->flags & IEEE80211_CHAN_DISABLED)
5370 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005371 }
5372
Johannes Bergfffd0932009-07-08 14:22:54 +02005373 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5374 connkeys = nl80211_parse_connkeys(rdev,
5375 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02005376 if (IS_ERR(connkeys))
5377 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005378 }
5379
Ben Greear7e7c8922011-11-18 11:31:59 -08005380 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
5381 connect.flags |= ASSOC_REQ_DISABLE_HT;
5382
5383 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5384 memcpy(&connect.ht_capa_mask,
5385 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
5386 sizeof(connect.ht_capa_mask));
5387
5388 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
5389 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5390 return -EINVAL;
5391 memcpy(&connect.ht_capa,
5392 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
5393 sizeof(connect.ht_capa));
5394 }
5395
Johannes Bergfffd0932009-07-08 14:22:54 +02005396 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005397 if (err)
5398 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005399 return err;
5400}
5401
5402static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
5403{
Johannes Berg4c476992010-10-04 21:36:35 +02005404 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5405 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005406 u16 reason;
5407
5408 if (!info->attrs[NL80211_ATTR_REASON_CODE])
5409 reason = WLAN_REASON_DEAUTH_LEAVING;
5410 else
5411 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5412
5413 if (reason == 0)
5414 return -EINVAL;
5415
Johannes Berg074ac8d2010-09-16 14:58:22 +02005416 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005417 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5418 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005419
Johannes Berg4c476992010-10-04 21:36:35 +02005420 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005421}
5422
Johannes Berg463d0182009-07-14 00:33:35 +02005423static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
5424{
Johannes Berg4c476992010-10-04 21:36:35 +02005425 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02005426 struct net *net;
5427 int err;
5428 u32 pid;
5429
5430 if (!info->attrs[NL80211_ATTR_PID])
5431 return -EINVAL;
5432
5433 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
5434
Johannes Berg463d0182009-07-14 00:33:35 +02005435 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02005436 if (IS_ERR(net))
5437 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005438
5439 err = 0;
5440
5441 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02005442 if (!net_eq(wiphy_net(&rdev->wiphy), net))
5443 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02005444
Johannes Berg463d0182009-07-14 00:33:35 +02005445 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005446 return err;
5447}
5448
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005449static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
5450{
Johannes Berg4c476992010-10-04 21:36:35 +02005451 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005452 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
5453 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02005454 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005455 struct cfg80211_pmksa pmksa;
5456
5457 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
5458
5459 if (!info->attrs[NL80211_ATTR_MAC])
5460 return -EINVAL;
5461
5462 if (!info->attrs[NL80211_ATTR_PMKID])
5463 return -EINVAL;
5464
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005465 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
5466 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5467
Johannes Berg074ac8d2010-09-16 14:58:22 +02005468 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005469 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5470 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005471
5472 switch (info->genlhdr->cmd) {
5473 case NL80211_CMD_SET_PMKSA:
5474 rdev_ops = rdev->ops->set_pmksa;
5475 break;
5476 case NL80211_CMD_DEL_PMKSA:
5477 rdev_ops = rdev->ops->del_pmksa;
5478 break;
5479 default:
5480 WARN_ON(1);
5481 break;
5482 }
5483
Johannes Berg4c476992010-10-04 21:36:35 +02005484 if (!rdev_ops)
5485 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005486
Johannes Berg4c476992010-10-04 21:36:35 +02005487 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005488}
5489
5490static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
5491{
Johannes Berg4c476992010-10-04 21:36:35 +02005492 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5493 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005494
Johannes Berg074ac8d2010-09-16 14:58:22 +02005495 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005496 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5497 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005498
Johannes Berg4c476992010-10-04 21:36:35 +02005499 if (!rdev->ops->flush_pmksa)
5500 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005501
Johannes Berg4c476992010-10-04 21:36:35 +02005502 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005503}
5504
Arik Nemtsov109086c2011-09-28 14:12:50 +03005505static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
5506{
5507 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5508 struct net_device *dev = info->user_ptr[1];
5509 u8 action_code, dialog_token;
5510 u16 status_code;
5511 u8 *peer;
5512
5513 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5514 !rdev->ops->tdls_mgmt)
5515 return -EOPNOTSUPP;
5516
5517 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
5518 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
5519 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
5520 !info->attrs[NL80211_ATTR_IE] ||
5521 !info->attrs[NL80211_ATTR_MAC])
5522 return -EINVAL;
5523
5524 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5525 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
5526 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
5527 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
5528
5529 return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
5530 dialog_token, status_code,
5531 nla_data(info->attrs[NL80211_ATTR_IE]),
5532 nla_len(info->attrs[NL80211_ATTR_IE]));
5533}
5534
5535static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
5536{
5537 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5538 struct net_device *dev = info->user_ptr[1];
5539 enum nl80211_tdls_operation operation;
5540 u8 *peer;
5541
5542 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5543 !rdev->ops->tdls_oper)
5544 return -EOPNOTSUPP;
5545
5546 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
5547 !info->attrs[NL80211_ATTR_MAC])
5548 return -EINVAL;
5549
5550 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
5551 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5552
5553 return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
5554}
5555
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005556static int nl80211_remain_on_channel(struct sk_buff *skb,
5557 struct genl_info *info)
5558{
Johannes Berg4c476992010-10-04 21:36:35 +02005559 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5560 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005561 struct ieee80211_channel *chan;
5562 struct sk_buff *msg;
5563 void *hdr;
5564 u64 cookie;
5565 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
5566 u32 freq, duration;
5567 int err;
5568
5569 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5570 !info->attrs[NL80211_ATTR_DURATION])
5571 return -EINVAL;
5572
5573 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5574
Johannes Berg7c4ef712011-11-18 15:33:48 +01005575 if (!rdev->ops->remain_on_channel ||
5576 !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
Johannes Berg4c476992010-10-04 21:36:35 +02005577 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005578
Johannes Bergebf348f2012-06-01 12:50:54 +02005579 /*
5580 * We should be on that channel for at least a minimum amount of
5581 * time (10ms) but no longer than the driver supports.
5582 */
5583 if (duration < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
5584 duration > rdev->wiphy.max_remain_on_channel_duration)
5585 return -EINVAL;
5586
Johannes Bergcd6c65982012-05-10 21:27:18 +02005587 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
5588 !nl80211_valid_channel_type(info, &channel_type))
5589 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005590
5591 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5592 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005593 if (chan == NULL)
5594 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005595
5596 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005597 if (!msg)
5598 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005599
5600 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5601 NL80211_CMD_REMAIN_ON_CHANNEL);
5602
5603 if (IS_ERR(hdr)) {
5604 err = PTR_ERR(hdr);
5605 goto free_msg;
5606 }
5607
5608 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
5609 channel_type, duration, &cookie);
5610
5611 if (err)
5612 goto free_msg;
5613
David S. Miller9360ffd2012-03-29 04:41:26 -04005614 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
5615 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005616
5617 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005618
5619 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005620
5621 nla_put_failure:
5622 err = -ENOBUFS;
5623 free_msg:
5624 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005625 return err;
5626}
5627
5628static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
5629 struct genl_info *info)
5630{
Johannes Berg4c476992010-10-04 21:36:35 +02005631 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5632 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005633 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005634
5635 if (!info->attrs[NL80211_ATTR_COOKIE])
5636 return -EINVAL;
5637
Johannes Berg4c476992010-10-04 21:36:35 +02005638 if (!rdev->ops->cancel_remain_on_channel)
5639 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005640
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005641 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5642
Johannes Berg4c476992010-10-04 21:36:35 +02005643 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005644}
5645
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005646static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
5647 u8 *rates, u8 rates_len)
5648{
5649 u8 i;
5650 u32 mask = 0;
5651
5652 for (i = 0; i < rates_len; i++) {
5653 int rate = (rates[i] & 0x7f) * 5;
5654 int ridx;
5655 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
5656 struct ieee80211_rate *srate =
5657 &sband->bitrates[ridx];
5658 if (rate == srate->bitrate) {
5659 mask |= 1 << ridx;
5660 break;
5661 }
5662 }
5663 if (ridx == sband->n_bitrates)
5664 return 0; /* rate not found */
5665 }
5666
5667 return mask;
5668}
5669
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005670static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
5671 u8 *rates, u8 rates_len,
5672 u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
5673{
5674 u8 i;
5675
5676 memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
5677
5678 for (i = 0; i < rates_len; i++) {
5679 int ridx, rbit;
5680
5681 ridx = rates[i] / 8;
5682 rbit = BIT(rates[i] % 8);
5683
5684 /* check validity */
Dan Carpenter910570b52012-02-01 10:42:11 +03005685 if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005686 return false;
5687
5688 /* check availability */
5689 if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
5690 mcs[ridx] |= rbit;
5691 else
5692 return false;
5693 }
5694
5695 return true;
5696}
5697
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00005698static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005699 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
5700 .len = NL80211_MAX_SUPP_RATES },
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005701 [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
5702 .len = NL80211_MAX_SUPP_HT_RATES },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005703};
5704
5705static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
5706 struct genl_info *info)
5707{
5708 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02005709 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005710 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02005711 int rem, i;
5712 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005713 struct nlattr *tx_rates;
5714 struct ieee80211_supported_band *sband;
5715
5716 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
5717 return -EINVAL;
5718
Johannes Berg4c476992010-10-04 21:36:35 +02005719 if (!rdev->ops->set_bitrate_mask)
5720 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005721
5722 memset(&mask, 0, sizeof(mask));
5723 /* Default to all rates enabled */
5724 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
5725 sband = rdev->wiphy.bands[i];
5726 mask.control[i].legacy =
5727 sband ? (1 << sband->n_bitrates) - 1 : 0;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005728 if (sband)
5729 memcpy(mask.control[i].mcs,
5730 sband->ht_cap.mcs.rx_mask,
5731 sizeof(mask.control[i].mcs));
5732 else
5733 memset(mask.control[i].mcs, 0,
5734 sizeof(mask.control[i].mcs));
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005735 }
5736
5737 /*
5738 * The nested attribute uses enum nl80211_band as the index. This maps
5739 * directly to the enum ieee80211_band values used in cfg80211.
5740 */
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005741 BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005742 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
5743 {
5744 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02005745 if (band < 0 || band >= IEEE80211_NUM_BANDS)
5746 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005747 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02005748 if (sband == NULL)
5749 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005750 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
5751 nla_len(tx_rates), nl80211_txattr_policy);
5752 if (tb[NL80211_TXRATE_LEGACY]) {
5753 mask.control[band].legacy = rateset_to_mask(
5754 sband,
5755 nla_data(tb[NL80211_TXRATE_LEGACY]),
5756 nla_len(tb[NL80211_TXRATE_LEGACY]));
Bala Shanmugam218d2e22012-04-20 19:12:58 +05305757 if ((mask.control[band].legacy == 0) &&
5758 nla_len(tb[NL80211_TXRATE_LEGACY]))
5759 return -EINVAL;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005760 }
5761 if (tb[NL80211_TXRATE_MCS]) {
5762 if (!ht_rateset_to_mask(
5763 sband,
5764 nla_data(tb[NL80211_TXRATE_MCS]),
5765 nla_len(tb[NL80211_TXRATE_MCS]),
5766 mask.control[band].mcs))
5767 return -EINVAL;
5768 }
5769
5770 if (mask.control[band].legacy == 0) {
5771 /* don't allow empty legacy rates if HT
5772 * is not even supported. */
5773 if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
5774 return -EINVAL;
5775
5776 for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
5777 if (mask.control[band].mcs[i])
5778 break;
5779
5780 /* legacy and mcs rates may not be both empty */
5781 if (i == IEEE80211_HT_MCS_MASK_LEN)
Johannes Berg4c476992010-10-04 21:36:35 +02005782 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005783 }
5784 }
5785
Johannes Berg4c476992010-10-04 21:36:35 +02005786 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005787}
5788
Johannes Berg2e161f782010-08-12 15:38:38 +02005789static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005790{
Johannes Berg4c476992010-10-04 21:36:35 +02005791 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5792 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f782010-08-12 15:38:38 +02005793 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02005794
5795 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
5796 return -EINVAL;
5797
Johannes Berg2e161f782010-08-12 15:38:38 +02005798 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
5799 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02005800
Johannes Berg9d38d852010-06-09 17:20:33 +02005801 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005802 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005803 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5804 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5805 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005806 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005807 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5808 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005809
5810 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005811 if (!rdev->ops->mgmt_tx)
5812 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005813
Johannes Berg4c476992010-10-04 21:36:35 +02005814 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f782010-08-12 15:38:38 +02005815 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005816 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5817 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005818}
5819
Johannes Berg2e161f782010-08-12 15:38:38 +02005820static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005821{
Johannes Berg4c476992010-10-04 21:36:35 +02005822 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5823 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005824 struct ieee80211_channel *chan;
5825 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005826 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005827 u32 freq;
5828 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01005829 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005830 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01005831 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005832 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01005833 bool offchan, no_cck, dont_wait_for_ack;
5834
5835 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02005836
5837 if (!info->attrs[NL80211_ATTR_FRAME] ||
5838 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5839 return -EINVAL;
5840
Johannes Berg4c476992010-10-04 21:36:35 +02005841 if (!rdev->ops->mgmt_tx)
5842 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005843
Johannes Berg9d38d852010-06-09 17:20:33 +02005844 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005845 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005846 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5847 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5848 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005849 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005850 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5851 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005852
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005853 if (info->attrs[NL80211_ATTR_DURATION]) {
Johannes Berg7c4ef712011-11-18 15:33:48 +01005854 if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005855 return -EINVAL;
5856 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
Johannes Bergebf348f2012-06-01 12:50:54 +02005857
5858 /*
5859 * We should wait on the channel for at least a minimum amount
5860 * of time (10ms) but no longer than the driver supports.
5861 */
5862 if (wait < NL80211_MIN_REMAIN_ON_CHANNEL_TIME ||
5863 wait > rdev->wiphy.max_remain_on_channel_duration)
5864 return -EINVAL;
5865
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005866 }
5867
Jouni Malinen026331c2010-02-15 12:53:10 +02005868 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
Johannes Bergcd6c65982012-05-10 21:27:18 +02005869 if (!nl80211_valid_channel_type(info, &channel_type))
Johannes Berg4c476992010-10-04 21:36:35 +02005870 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005871 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005872 }
5873
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005874 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5875
Johannes Berg7c4ef712011-11-18 15:33:48 +01005876 if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
5877 return -EINVAL;
5878
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305879 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5880
Jouni Malinen026331c2010-02-15 12:53:10 +02005881 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5882 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005883 if (chan == NULL)
5884 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005885
Johannes Berge247bd902011-11-04 11:18:21 +01005886 if (!dont_wait_for_ack) {
5887 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5888 if (!msg)
5889 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005890
Johannes Berge247bd902011-11-04 11:18:21 +01005891 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5892 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005893
Johannes Berge247bd902011-11-04 11:18:21 +01005894 if (IS_ERR(hdr)) {
5895 err = PTR_ERR(hdr);
5896 goto free_msg;
5897 }
Jouni Malinen026331c2010-02-15 12:53:10 +02005898 }
Johannes Berge247bd902011-11-04 11:18:21 +01005899
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005900 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5901 channel_type_valid, wait,
Johannes Berg2e161f782010-08-12 15:38:38 +02005902 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5903 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01005904 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005905 if (err)
5906 goto free_msg;
5907
Johannes Berge247bd902011-11-04 11:18:21 +01005908 if (msg) {
David S. Miller9360ffd2012-03-29 04:41:26 -04005909 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
5910 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02005911
Johannes Berge247bd902011-11-04 11:18:21 +01005912 genlmsg_end(msg, hdr);
5913 return genlmsg_reply(msg, info);
5914 }
5915
5916 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02005917
5918 nla_put_failure:
5919 err = -ENOBUFS;
5920 free_msg:
5921 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005922 return err;
5923}
5924
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005925static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5926{
5927 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5928 struct net_device *dev = info->user_ptr[1];
5929 u64 cookie;
5930
5931 if (!info->attrs[NL80211_ATTR_COOKIE])
5932 return -EINVAL;
5933
5934 if (!rdev->ops->mgmt_tx_cancel_wait)
5935 return -EOPNOTSUPP;
5936
5937 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5938 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5939 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5940 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5941 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5942 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5943 return -EOPNOTSUPP;
5944
5945 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5946
5947 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5948}
5949
Kalle Valoffb9eb32010-02-17 17:58:10 +02005950static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5951{
Johannes Berg4c476992010-10-04 21:36:35 +02005952 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005953 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005954 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005955 u8 ps_state;
5956 bool state;
5957 int err;
5958
Johannes Berg4c476992010-10-04 21:36:35 +02005959 if (!info->attrs[NL80211_ATTR_PS_STATE])
5960 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005961
5962 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5963
Johannes Berg4c476992010-10-04 21:36:35 +02005964 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5965 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005966
5967 wdev = dev->ieee80211_ptr;
5968
Johannes Berg4c476992010-10-04 21:36:35 +02005969 if (!rdev->ops->set_power_mgmt)
5970 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005971
5972 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5973
5974 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005975 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005976
Johannes Berg4c476992010-10-04 21:36:35 +02005977 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5978 wdev->ps_timeout);
5979 if (!err)
5980 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005981 return err;
5982}
5983
5984static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5985{
Johannes Berg4c476992010-10-04 21:36:35 +02005986 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005987 enum nl80211_ps_state ps_state;
5988 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005989 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005990 struct sk_buff *msg;
5991 void *hdr;
5992 int err;
5993
Kalle Valoffb9eb32010-02-17 17:58:10 +02005994 wdev = dev->ieee80211_ptr;
5995
Johannes Berg4c476992010-10-04 21:36:35 +02005996 if (!rdev->ops->set_power_mgmt)
5997 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005998
5999 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02006000 if (!msg)
6001 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006002
6003 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6004 NL80211_CMD_GET_POWER_SAVE);
6005 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02006006 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006007 goto free_msg;
6008 }
6009
6010 if (wdev->ps)
6011 ps_state = NL80211_PS_ENABLED;
6012 else
6013 ps_state = NL80211_PS_DISABLED;
6014
David S. Miller9360ffd2012-03-29 04:41:26 -04006015 if (nla_put_u32(msg, NL80211_ATTR_PS_STATE, ps_state))
6016 goto nla_put_failure;
Kalle Valoffb9eb32010-02-17 17:58:10 +02006017
6018 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02006019 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02006020
Johannes Berg4c476992010-10-04 21:36:35 +02006021 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02006022 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02006023 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02006024 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02006025 return err;
6026}
6027
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006028static struct nla_policy
6029nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
6030 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
6031 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
6032 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
6033};
6034
6035static int nl80211_set_cqm_rssi(struct genl_info *info,
6036 s32 threshold, u32 hysteresis)
6037{
Johannes Berg4c476992010-10-04 21:36:35 +02006038 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006039 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02006040 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006041
6042 if (threshold > 0)
6043 return -EINVAL;
6044
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006045 wdev = dev->ieee80211_ptr;
6046
Johannes Berg4c476992010-10-04 21:36:35 +02006047 if (!rdev->ops->set_cqm_rssi_config)
6048 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006049
Johannes Berg074ac8d2010-09-16 14:58:22 +02006050 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006051 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
6052 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006053
Johannes Berg4c476992010-10-04 21:36:35 +02006054 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
6055 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006056}
6057
6058static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
6059{
6060 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
6061 struct nlattr *cqm;
6062 int err;
6063
6064 cqm = info->attrs[NL80211_ATTR_CQM];
6065 if (!cqm) {
6066 err = -EINVAL;
6067 goto out;
6068 }
6069
6070 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
6071 nl80211_attr_cqm_policy);
6072 if (err)
6073 goto out;
6074
6075 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
6076 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
6077 s32 threshold;
6078 u32 hysteresis;
6079 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
6080 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
6081 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
6082 } else
6083 err = -EINVAL;
6084
6085out:
6086 return err;
6087}
6088
Johannes Berg29cbe682010-12-03 09:20:44 +01006089static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
6090{
6091 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6092 struct net_device *dev = info->user_ptr[1];
6093 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08006094 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01006095 int err;
6096
6097 /* start with default */
6098 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08006099 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01006100
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006101 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01006102 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006103 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01006104 if (err)
6105 return err;
6106 }
6107
6108 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
6109 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
6110 return -EINVAL;
6111
Javier Cardonac80d5452010-12-16 17:37:49 -08006112 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
6113 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
6114
Chun-Yeow Yeoh4bb62342011-11-24 17:15:20 -08006115 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
6116 !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
6117 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
6118 return -EINVAL;
6119
Javier Cardonac80d5452010-12-16 17:37:49 -08006120 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
6121 /* parse additional setup parameters if given */
6122 err = nl80211_parse_mesh_setup(info, &setup);
6123 if (err)
6124 return err;
6125 }
6126
Johannes Bergcc1d2802012-05-16 23:50:20 +02006127 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
6128 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
6129
6130 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE] &&
6131 !nl80211_valid_channel_type(info, &channel_type))
6132 return -EINVAL;
6133
6134 setup.channel = rdev_freq_to_chan(rdev,
6135 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
6136 channel_type);
6137 if (!setup.channel)
6138 return -EINVAL;
6139 setup.channel_type = channel_type;
6140 } else {
6141 /* cfg80211_join_mesh() will sort it out */
6142 setup.channel = NULL;
6143 }
6144
Javier Cardonac80d5452010-12-16 17:37:49 -08006145 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01006146}
6147
6148static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
6149{
6150 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6151 struct net_device *dev = info->user_ptr[1];
6152
6153 return cfg80211_leave_mesh(rdev, dev);
6154}
6155
Johannes Bergff1b6e62011-05-04 15:37:28 +02006156static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
6157{
6158 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6159 struct sk_buff *msg;
6160 void *hdr;
6161
6162 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6163 return -EOPNOTSUPP;
6164
6165 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6166 if (!msg)
6167 return -ENOMEM;
6168
6169 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6170 NL80211_CMD_GET_WOWLAN);
6171 if (!hdr)
6172 goto nla_put_failure;
6173
6174 if (rdev->wowlan) {
6175 struct nlattr *nl_wowlan;
6176
6177 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
6178 if (!nl_wowlan)
6179 goto nla_put_failure;
6180
David S. Miller9360ffd2012-03-29 04:41:26 -04006181 if ((rdev->wowlan->any &&
6182 nla_put_flag(msg, NL80211_WOWLAN_TRIG_ANY)) ||
6183 (rdev->wowlan->disconnect &&
6184 nla_put_flag(msg, NL80211_WOWLAN_TRIG_DISCONNECT)) ||
6185 (rdev->wowlan->magic_pkt &&
6186 nla_put_flag(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT)) ||
6187 (rdev->wowlan->gtk_rekey_failure &&
6188 nla_put_flag(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE)) ||
6189 (rdev->wowlan->eap_identity_req &&
6190 nla_put_flag(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST)) ||
6191 (rdev->wowlan->four_way_handshake &&
6192 nla_put_flag(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE)) ||
6193 (rdev->wowlan->rfkill_release &&
6194 nla_put_flag(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE)))
6195 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02006196 if (rdev->wowlan->n_patterns) {
6197 struct nlattr *nl_pats, *nl_pat;
6198 int i, pat_len;
6199
6200 nl_pats = nla_nest_start(msg,
6201 NL80211_WOWLAN_TRIG_PKT_PATTERN);
6202 if (!nl_pats)
6203 goto nla_put_failure;
6204
6205 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
6206 nl_pat = nla_nest_start(msg, i + 1);
6207 if (!nl_pat)
6208 goto nla_put_failure;
6209 pat_len = rdev->wowlan->patterns[i].pattern_len;
David S. Miller9360ffd2012-03-29 04:41:26 -04006210 if (nla_put(msg, NL80211_WOWLAN_PKTPAT_MASK,
6211 DIV_ROUND_UP(pat_len, 8),
6212 rdev->wowlan->patterns[i].mask) ||
6213 nla_put(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
6214 pat_len,
6215 rdev->wowlan->patterns[i].pattern))
6216 goto nla_put_failure;
Johannes Bergff1b6e62011-05-04 15:37:28 +02006217 nla_nest_end(msg, nl_pat);
6218 }
6219 nla_nest_end(msg, nl_pats);
6220 }
6221
6222 nla_nest_end(msg, nl_wowlan);
6223 }
6224
6225 genlmsg_end(msg, hdr);
6226 return genlmsg_reply(msg, info);
6227
6228nla_put_failure:
6229 nlmsg_free(msg);
6230 return -ENOBUFS;
6231}
6232
6233static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
6234{
6235 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6236 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
6237 struct cfg80211_wowlan no_triggers = {};
6238 struct cfg80211_wowlan new_triggers = {};
6239 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
6240 int err, i;
Johannes Berg6d525632012-04-04 15:05:25 +02006241 bool prev_enabled = rdev->wowlan;
Johannes Bergff1b6e62011-05-04 15:37:28 +02006242
6243 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6244 return -EOPNOTSUPP;
6245
6246 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
6247 goto no_triggers;
6248
6249 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
6250 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6251 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6252 nl80211_wowlan_policy);
6253 if (err)
6254 return err;
6255
6256 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
6257 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
6258 return -EINVAL;
6259 new_triggers.any = true;
6260 }
6261
6262 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
6263 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
6264 return -EINVAL;
6265 new_triggers.disconnect = true;
6266 }
6267
6268 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
6269 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
6270 return -EINVAL;
6271 new_triggers.magic_pkt = true;
6272 }
6273
Johannes Berg77dbbb12011-07-13 10:48:55 +02006274 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
6275 return -EINVAL;
6276
6277 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
6278 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
6279 return -EINVAL;
6280 new_triggers.gtk_rekey_failure = true;
6281 }
6282
6283 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
6284 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
6285 return -EINVAL;
6286 new_triggers.eap_identity_req = true;
6287 }
6288
6289 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
6290 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
6291 return -EINVAL;
6292 new_triggers.four_way_handshake = true;
6293 }
6294
6295 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
6296 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
6297 return -EINVAL;
6298 new_triggers.rfkill_release = true;
6299 }
6300
Johannes Bergff1b6e62011-05-04 15:37:28 +02006301 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
6302 struct nlattr *pat;
6303 int n_patterns = 0;
6304 int rem, pat_len, mask_len;
6305 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
6306
6307 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6308 rem)
6309 n_patterns++;
6310 if (n_patterns > wowlan->n_patterns)
6311 return -EINVAL;
6312
6313 new_triggers.patterns = kcalloc(n_patterns,
6314 sizeof(new_triggers.patterns[0]),
6315 GFP_KERNEL);
6316 if (!new_triggers.patterns)
6317 return -ENOMEM;
6318
6319 new_triggers.n_patterns = n_patterns;
6320 i = 0;
6321
6322 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6323 rem) {
6324 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
6325 nla_data(pat), nla_len(pat), NULL);
6326 err = -EINVAL;
6327 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
6328 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
6329 goto error;
6330 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
6331 mask_len = DIV_ROUND_UP(pat_len, 8);
6332 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
6333 mask_len)
6334 goto error;
6335 if (pat_len > wowlan->pattern_max_len ||
6336 pat_len < wowlan->pattern_min_len)
6337 goto error;
6338
6339 new_triggers.patterns[i].mask =
6340 kmalloc(mask_len + pat_len, GFP_KERNEL);
6341 if (!new_triggers.patterns[i].mask) {
6342 err = -ENOMEM;
6343 goto error;
6344 }
6345 new_triggers.patterns[i].pattern =
6346 new_triggers.patterns[i].mask + mask_len;
6347 memcpy(new_triggers.patterns[i].mask,
6348 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
6349 mask_len);
6350 new_triggers.patterns[i].pattern_len = pat_len;
6351 memcpy(new_triggers.patterns[i].pattern,
6352 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
6353 pat_len);
6354 i++;
6355 }
6356 }
6357
6358 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
6359 struct cfg80211_wowlan *ntrig;
6360 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
6361 GFP_KERNEL);
6362 if (!ntrig) {
6363 err = -ENOMEM;
6364 goto error;
6365 }
6366 cfg80211_rdev_free_wowlan(rdev);
6367 rdev->wowlan = ntrig;
6368 } else {
6369 no_triggers:
6370 cfg80211_rdev_free_wowlan(rdev);
6371 rdev->wowlan = NULL;
6372 }
6373
Johannes Berg6d525632012-04-04 15:05:25 +02006374 if (rdev->ops->set_wakeup && prev_enabled != !!rdev->wowlan)
6375 rdev->ops->set_wakeup(&rdev->wiphy, rdev->wowlan);
6376
Johannes Bergff1b6e62011-05-04 15:37:28 +02006377 return 0;
6378 error:
6379 for (i = 0; i < new_triggers.n_patterns; i++)
6380 kfree(new_triggers.patterns[i].mask);
6381 kfree(new_triggers.patterns);
6382 return err;
6383}
6384
Johannes Berge5497d72011-07-05 16:35:40 +02006385static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
6386{
6387 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6388 struct net_device *dev = info->user_ptr[1];
6389 struct wireless_dev *wdev = dev->ieee80211_ptr;
6390 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
6391 struct cfg80211_gtk_rekey_data rekey_data;
6392 int err;
6393
6394 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
6395 return -EINVAL;
6396
6397 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
6398 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
6399 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
6400 nl80211_rekey_policy);
6401 if (err)
6402 return err;
6403
6404 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
6405 return -ERANGE;
6406 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
6407 return -ERANGE;
6408 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
6409 return -ERANGE;
6410
6411 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
6412 NL80211_KEK_LEN);
6413 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
6414 NL80211_KCK_LEN);
6415 memcpy(rekey_data.replay_ctr,
6416 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
6417 NL80211_REPLAY_CTR_LEN);
6418
6419 wdev_lock(wdev);
6420 if (!wdev->current_bss) {
6421 err = -ENOTCONN;
6422 goto out;
6423 }
6424
6425 if (!rdev->ops->set_rekey_data) {
6426 err = -EOPNOTSUPP;
6427 goto out;
6428 }
6429
6430 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
6431 out:
6432 wdev_unlock(wdev);
6433 return err;
6434}
6435
Johannes Berg28946da2011-11-04 11:18:12 +01006436static int nl80211_register_unexpected_frame(struct sk_buff *skb,
6437 struct genl_info *info)
6438{
6439 struct net_device *dev = info->user_ptr[1];
6440 struct wireless_dev *wdev = dev->ieee80211_ptr;
6441
6442 if (wdev->iftype != NL80211_IFTYPE_AP &&
6443 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6444 return -EINVAL;
6445
6446 if (wdev->ap_unexpected_nlpid)
6447 return -EBUSY;
6448
6449 wdev->ap_unexpected_nlpid = info->snd_pid;
6450 return 0;
6451}
6452
Johannes Berg7f6cf312011-11-04 11:18:15 +01006453static int nl80211_probe_client(struct sk_buff *skb,
6454 struct genl_info *info)
6455{
6456 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6457 struct net_device *dev = info->user_ptr[1];
6458 struct wireless_dev *wdev = dev->ieee80211_ptr;
6459 struct sk_buff *msg;
6460 void *hdr;
6461 const u8 *addr;
6462 u64 cookie;
6463 int err;
6464
6465 if (wdev->iftype != NL80211_IFTYPE_AP &&
6466 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6467 return -EOPNOTSUPP;
6468
6469 if (!info->attrs[NL80211_ATTR_MAC])
6470 return -EINVAL;
6471
6472 if (!rdev->ops->probe_client)
6473 return -EOPNOTSUPP;
6474
6475 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6476 if (!msg)
6477 return -ENOMEM;
6478
6479 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6480 NL80211_CMD_PROBE_CLIENT);
6481
6482 if (IS_ERR(hdr)) {
6483 err = PTR_ERR(hdr);
6484 goto free_msg;
6485 }
6486
6487 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
6488
6489 err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
6490 if (err)
6491 goto free_msg;
6492
David S. Miller9360ffd2012-03-29 04:41:26 -04006493 if (nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
6494 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01006495
6496 genlmsg_end(msg, hdr);
6497
6498 return genlmsg_reply(msg, info);
6499
6500 nla_put_failure:
6501 err = -ENOBUFS;
6502 free_msg:
6503 nlmsg_free(msg);
6504 return err;
6505}
6506
Johannes Berg5e760232011-11-04 11:18:17 +01006507static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
6508{
6509 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6510
6511 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
6512 return -EOPNOTSUPP;
6513
6514 if (rdev->ap_beacons_nlpid)
6515 return -EBUSY;
6516
6517 rdev->ap_beacons_nlpid = info->snd_pid;
6518
6519 return 0;
6520}
6521
Johannes Berg4c476992010-10-04 21:36:35 +02006522#define NL80211_FLAG_NEED_WIPHY 0x01
6523#define NL80211_FLAG_NEED_NETDEV 0x02
6524#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02006525#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
6526#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
6527 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02006528
6529static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
6530 struct genl_info *info)
6531{
6532 struct cfg80211_registered_device *rdev;
6533 struct net_device *dev;
6534 int err;
6535 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
6536
6537 if (rtnl)
6538 rtnl_lock();
6539
6540 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
6541 rdev = cfg80211_get_dev_from_info(info);
6542 if (IS_ERR(rdev)) {
6543 if (rtnl)
6544 rtnl_unlock();
6545 return PTR_ERR(rdev);
6546 }
6547 info->user_ptr[0] = rdev;
6548 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
Johannes Berg00918d32011-12-13 17:22:05 +01006549 err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs,
6550 &rdev, &dev);
Johannes Berg4c476992010-10-04 21:36:35 +02006551 if (err) {
6552 if (rtnl)
6553 rtnl_unlock();
6554 return err;
6555 }
Johannes Berg41265712010-10-04 21:14:05 +02006556 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
6557 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02006558 cfg80211_unlock_rdev(rdev);
6559 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02006560 if (rtnl)
6561 rtnl_unlock();
6562 return -ENETDOWN;
6563 }
Johannes Berg4c476992010-10-04 21:36:35 +02006564 info->user_ptr[0] = rdev;
6565 info->user_ptr[1] = dev;
6566 }
6567
6568 return 0;
6569}
6570
6571static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
6572 struct genl_info *info)
6573{
6574 if (info->user_ptr[0])
6575 cfg80211_unlock_rdev(info->user_ptr[0]);
6576 if (info->user_ptr[1])
6577 dev_put(info->user_ptr[1]);
6578 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
6579 rtnl_unlock();
6580}
6581
Johannes Berg55682962007-09-20 13:09:35 -04006582static struct genl_ops nl80211_ops[] = {
6583 {
6584 .cmd = NL80211_CMD_GET_WIPHY,
6585 .doit = nl80211_get_wiphy,
6586 .dumpit = nl80211_dump_wiphy,
6587 .policy = nl80211_policy,
6588 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006589 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04006590 },
6591 {
6592 .cmd = NL80211_CMD_SET_WIPHY,
6593 .doit = nl80211_set_wiphy,
6594 .policy = nl80211_policy,
6595 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006596 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006597 },
6598 {
6599 .cmd = NL80211_CMD_GET_INTERFACE,
6600 .doit = nl80211_get_interface,
6601 .dumpit = nl80211_dump_interface,
6602 .policy = nl80211_policy,
6603 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006604 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04006605 },
6606 {
6607 .cmd = NL80211_CMD_SET_INTERFACE,
6608 .doit = nl80211_set_interface,
6609 .policy = nl80211_policy,
6610 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006611 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6612 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006613 },
6614 {
6615 .cmd = NL80211_CMD_NEW_INTERFACE,
6616 .doit = nl80211_new_interface,
6617 .policy = nl80211_policy,
6618 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006619 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6620 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006621 },
6622 {
6623 .cmd = NL80211_CMD_DEL_INTERFACE,
6624 .doit = nl80211_del_interface,
6625 .policy = nl80211_policy,
6626 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006627 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6628 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006629 },
Johannes Berg41ade002007-12-19 02:03:29 +01006630 {
6631 .cmd = NL80211_CMD_GET_KEY,
6632 .doit = nl80211_get_key,
6633 .policy = nl80211_policy,
6634 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006635 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006636 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006637 },
6638 {
6639 .cmd = NL80211_CMD_SET_KEY,
6640 .doit = nl80211_set_key,
6641 .policy = nl80211_policy,
6642 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006643 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006644 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006645 },
6646 {
6647 .cmd = NL80211_CMD_NEW_KEY,
6648 .doit = nl80211_new_key,
6649 .policy = nl80211_policy,
6650 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006651 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006652 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006653 },
6654 {
6655 .cmd = NL80211_CMD_DEL_KEY,
6656 .doit = nl80211_del_key,
6657 .policy = nl80211_policy,
6658 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006659 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006660 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006661 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01006662 {
6663 .cmd = NL80211_CMD_SET_BEACON,
6664 .policy = nl80211_policy,
6665 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006666 .doit = nl80211_set_beacon,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006667 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006668 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006669 },
6670 {
Johannes Berg88600202012-02-13 15:17:18 +01006671 .cmd = NL80211_CMD_START_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006672 .policy = nl80211_policy,
6673 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006674 .doit = nl80211_start_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006675 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006676 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006677 },
6678 {
Johannes Berg88600202012-02-13 15:17:18 +01006679 .cmd = NL80211_CMD_STOP_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006680 .policy = nl80211_policy,
6681 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006682 .doit = nl80211_stop_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006683 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006684 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006685 },
Johannes Berg5727ef12007-12-19 02:03:34 +01006686 {
6687 .cmd = NL80211_CMD_GET_STATION,
6688 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006689 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01006690 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02006691 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6692 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006693 },
6694 {
6695 .cmd = NL80211_CMD_SET_STATION,
6696 .doit = nl80211_set_station,
6697 .policy = nl80211_policy,
6698 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006699 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006700 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006701 },
6702 {
6703 .cmd = NL80211_CMD_NEW_STATION,
6704 .doit = nl80211_new_station,
6705 .policy = nl80211_policy,
6706 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006707 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006708 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006709 },
6710 {
6711 .cmd = NL80211_CMD_DEL_STATION,
6712 .doit = nl80211_del_station,
6713 .policy = nl80211_policy,
6714 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006715 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006716 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006717 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006718 {
6719 .cmd = NL80211_CMD_GET_MPATH,
6720 .doit = nl80211_get_mpath,
6721 .dumpit = nl80211_dump_mpath,
6722 .policy = nl80211_policy,
6723 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006724 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006725 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006726 },
6727 {
6728 .cmd = NL80211_CMD_SET_MPATH,
6729 .doit = nl80211_set_mpath,
6730 .policy = nl80211_policy,
6731 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006732 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006733 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006734 },
6735 {
6736 .cmd = NL80211_CMD_NEW_MPATH,
6737 .doit = nl80211_new_mpath,
6738 .policy = nl80211_policy,
6739 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006740 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006741 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006742 },
6743 {
6744 .cmd = NL80211_CMD_DEL_MPATH,
6745 .doit = nl80211_del_mpath,
6746 .policy = nl80211_policy,
6747 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006748 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006749 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006750 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006751 {
6752 .cmd = NL80211_CMD_SET_BSS,
6753 .doit = nl80211_set_bss,
6754 .policy = nl80211_policy,
6755 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006756 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006757 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006758 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006759 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08006760 .cmd = NL80211_CMD_GET_REG,
6761 .doit = nl80211_get_reg,
6762 .policy = nl80211_policy,
6763 /* can be retrieved by unprivileged users */
6764 },
6765 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006766 .cmd = NL80211_CMD_SET_REG,
6767 .doit = nl80211_set_reg,
6768 .policy = nl80211_policy,
6769 .flags = GENL_ADMIN_PERM,
6770 },
6771 {
6772 .cmd = NL80211_CMD_REQ_SET_REG,
6773 .doit = nl80211_req_set_reg,
6774 .policy = nl80211_policy,
6775 .flags = GENL_ADMIN_PERM,
6776 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006777 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006778 .cmd = NL80211_CMD_GET_MESH_CONFIG,
6779 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006780 .policy = nl80211_policy,
6781 /* can be retrieved by unprivileged users */
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006782 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006783 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006784 },
6785 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006786 .cmd = NL80211_CMD_SET_MESH_CONFIG,
6787 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006788 .policy = nl80211_policy,
6789 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01006790 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006791 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006792 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02006793 {
Johannes Berg2a519312009-02-10 21:25:55 +01006794 .cmd = NL80211_CMD_TRIGGER_SCAN,
6795 .doit = nl80211_trigger_scan,
6796 .policy = nl80211_policy,
6797 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006798 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006799 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01006800 },
6801 {
6802 .cmd = NL80211_CMD_GET_SCAN,
6803 .policy = nl80211_policy,
6804 .dumpit = nl80211_dump_scan,
6805 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02006806 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03006807 .cmd = NL80211_CMD_START_SCHED_SCAN,
6808 .doit = nl80211_start_sched_scan,
6809 .policy = nl80211_policy,
6810 .flags = GENL_ADMIN_PERM,
6811 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6812 NL80211_FLAG_NEED_RTNL,
6813 },
6814 {
6815 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
6816 .doit = nl80211_stop_sched_scan,
6817 .policy = nl80211_policy,
6818 .flags = GENL_ADMIN_PERM,
6819 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6820 NL80211_FLAG_NEED_RTNL,
6821 },
6822 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02006823 .cmd = NL80211_CMD_AUTHENTICATE,
6824 .doit = nl80211_authenticate,
6825 .policy = nl80211_policy,
6826 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006827 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006828 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006829 },
6830 {
6831 .cmd = NL80211_CMD_ASSOCIATE,
6832 .doit = nl80211_associate,
6833 .policy = nl80211_policy,
6834 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006835 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006836 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006837 },
6838 {
6839 .cmd = NL80211_CMD_DEAUTHENTICATE,
6840 .doit = nl80211_deauthenticate,
6841 .policy = nl80211_policy,
6842 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006843 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006844 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006845 },
6846 {
6847 .cmd = NL80211_CMD_DISASSOCIATE,
6848 .doit = nl80211_disassociate,
6849 .policy = nl80211_policy,
6850 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006851 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006852 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006853 },
Johannes Berg04a773a2009-04-19 21:24:32 +02006854 {
6855 .cmd = NL80211_CMD_JOIN_IBSS,
6856 .doit = nl80211_join_ibss,
6857 .policy = nl80211_policy,
6858 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006859 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006860 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006861 },
6862 {
6863 .cmd = NL80211_CMD_LEAVE_IBSS,
6864 .doit = nl80211_leave_ibss,
6865 .policy = nl80211_policy,
6866 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006867 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006868 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006869 },
Johannes Bergaff89a92009-07-01 21:26:51 +02006870#ifdef CONFIG_NL80211_TESTMODE
6871 {
6872 .cmd = NL80211_CMD_TESTMODE,
6873 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006874 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02006875 .policy = nl80211_policy,
6876 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006877 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6878 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02006879 },
6880#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02006881 {
6882 .cmd = NL80211_CMD_CONNECT,
6883 .doit = nl80211_connect,
6884 .policy = nl80211_policy,
6885 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006886 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006887 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006888 },
6889 {
6890 .cmd = NL80211_CMD_DISCONNECT,
6891 .doit = nl80211_disconnect,
6892 .policy = nl80211_policy,
6893 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006894 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006895 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006896 },
Johannes Berg463d0182009-07-14 00:33:35 +02006897 {
6898 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
6899 .doit = nl80211_wiphy_netns,
6900 .policy = nl80211_policy,
6901 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006902 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6903 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02006904 },
Holger Schurig61fa7132009-11-11 12:25:40 +01006905 {
6906 .cmd = NL80211_CMD_GET_SURVEY,
6907 .policy = nl80211_policy,
6908 .dumpit = nl80211_dump_survey,
6909 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006910 {
6911 .cmd = NL80211_CMD_SET_PMKSA,
6912 .doit = nl80211_setdel_pmksa,
6913 .policy = nl80211_policy,
6914 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006915 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006916 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006917 },
6918 {
6919 .cmd = NL80211_CMD_DEL_PMKSA,
6920 .doit = nl80211_setdel_pmksa,
6921 .policy = nl80211_policy,
6922 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006923 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006924 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006925 },
6926 {
6927 .cmd = NL80211_CMD_FLUSH_PMKSA,
6928 .doit = nl80211_flush_pmksa,
6929 .policy = nl80211_policy,
6930 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006931 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006932 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006933 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006934 {
6935 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
6936 .doit = nl80211_remain_on_channel,
6937 .policy = nl80211_policy,
6938 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006939 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006940 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006941 },
6942 {
6943 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6944 .doit = nl80211_cancel_remain_on_channel,
6945 .policy = nl80211_policy,
6946 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006947 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006948 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006949 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006950 {
6951 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
6952 .doit = nl80211_set_tx_bitrate_mask,
6953 .policy = nl80211_policy,
6954 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006955 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6956 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006957 },
Jouni Malinen026331c2010-02-15 12:53:10 +02006958 {
Johannes Berg2e161f782010-08-12 15:38:38 +02006959 .cmd = NL80211_CMD_REGISTER_FRAME,
6960 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006961 .policy = nl80211_policy,
6962 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006963 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6964 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006965 },
6966 {
Johannes Berg2e161f782010-08-12 15:38:38 +02006967 .cmd = NL80211_CMD_FRAME,
6968 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006969 .policy = nl80211_policy,
6970 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006971 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006972 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006973 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02006974 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006975 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
6976 .doit = nl80211_tx_mgmt_cancel_wait,
6977 .policy = nl80211_policy,
6978 .flags = GENL_ADMIN_PERM,
6979 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6980 NL80211_FLAG_NEED_RTNL,
6981 },
6982 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02006983 .cmd = NL80211_CMD_SET_POWER_SAVE,
6984 .doit = nl80211_set_power_save,
6985 .policy = nl80211_policy,
6986 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006987 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6988 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006989 },
6990 {
6991 .cmd = NL80211_CMD_GET_POWER_SAVE,
6992 .doit = nl80211_get_power_save,
6993 .policy = nl80211_policy,
6994 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006995 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6996 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006997 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006998 {
6999 .cmd = NL80211_CMD_SET_CQM,
7000 .doit = nl80211_set_cqm,
7001 .policy = nl80211_policy,
7002 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007003 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7004 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007005 },
Johannes Bergf444de02010-05-05 15:25:02 +02007006 {
7007 .cmd = NL80211_CMD_SET_CHANNEL,
7008 .doit = nl80211_set_channel,
7009 .policy = nl80211_policy,
7010 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02007011 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7012 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02007013 },
Bill Jordane8347eb2010-10-01 13:54:28 -04007014 {
7015 .cmd = NL80211_CMD_SET_WDS_PEER,
7016 .doit = nl80211_set_wds_peer,
7017 .policy = nl80211_policy,
7018 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02007019 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7020 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04007021 },
Johannes Berg29cbe682010-12-03 09:20:44 +01007022 {
7023 .cmd = NL80211_CMD_JOIN_MESH,
7024 .doit = nl80211_join_mesh,
7025 .policy = nl80211_policy,
7026 .flags = GENL_ADMIN_PERM,
7027 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7028 NL80211_FLAG_NEED_RTNL,
7029 },
7030 {
7031 .cmd = NL80211_CMD_LEAVE_MESH,
7032 .doit = nl80211_leave_mesh,
7033 .policy = nl80211_policy,
7034 .flags = GENL_ADMIN_PERM,
7035 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7036 NL80211_FLAG_NEED_RTNL,
7037 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02007038 {
7039 .cmd = NL80211_CMD_GET_WOWLAN,
7040 .doit = nl80211_get_wowlan,
7041 .policy = nl80211_policy,
7042 /* can be retrieved by unprivileged users */
7043 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7044 NL80211_FLAG_NEED_RTNL,
7045 },
7046 {
7047 .cmd = NL80211_CMD_SET_WOWLAN,
7048 .doit = nl80211_set_wowlan,
7049 .policy = nl80211_policy,
7050 .flags = GENL_ADMIN_PERM,
7051 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7052 NL80211_FLAG_NEED_RTNL,
7053 },
Johannes Berge5497d72011-07-05 16:35:40 +02007054 {
7055 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
7056 .doit = nl80211_set_rekey_data,
7057 .policy = nl80211_policy,
7058 .flags = GENL_ADMIN_PERM,
7059 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7060 NL80211_FLAG_NEED_RTNL,
7061 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03007062 {
7063 .cmd = NL80211_CMD_TDLS_MGMT,
7064 .doit = nl80211_tdls_mgmt,
7065 .policy = nl80211_policy,
7066 .flags = GENL_ADMIN_PERM,
7067 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7068 NL80211_FLAG_NEED_RTNL,
7069 },
7070 {
7071 .cmd = NL80211_CMD_TDLS_OPER,
7072 .doit = nl80211_tdls_oper,
7073 .policy = nl80211_policy,
7074 .flags = GENL_ADMIN_PERM,
7075 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7076 NL80211_FLAG_NEED_RTNL,
7077 },
Johannes Berg28946da2011-11-04 11:18:12 +01007078 {
7079 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
7080 .doit = nl80211_register_unexpected_frame,
7081 .policy = nl80211_policy,
7082 .flags = GENL_ADMIN_PERM,
7083 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7084 NL80211_FLAG_NEED_RTNL,
7085 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01007086 {
7087 .cmd = NL80211_CMD_PROBE_CLIENT,
7088 .doit = nl80211_probe_client,
7089 .policy = nl80211_policy,
7090 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02007091 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg7f6cf312011-11-04 11:18:15 +01007092 NL80211_FLAG_NEED_RTNL,
7093 },
Johannes Berg5e760232011-11-04 11:18:17 +01007094 {
7095 .cmd = NL80211_CMD_REGISTER_BEACONS,
7096 .doit = nl80211_register_beacons,
7097 .policy = nl80211_policy,
7098 .flags = GENL_ADMIN_PERM,
7099 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7100 NL80211_FLAG_NEED_RTNL,
7101 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01007102 {
7103 .cmd = NL80211_CMD_SET_NOACK_MAP,
7104 .doit = nl80211_set_noack_map,
7105 .policy = nl80211_policy,
7106 .flags = GENL_ADMIN_PERM,
7107 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7108 NL80211_FLAG_NEED_RTNL,
7109 },
7110
Johannes Berg55682962007-09-20 13:09:35 -04007111};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007112
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007113static struct genl_multicast_group nl80211_mlme_mcgrp = {
7114 .name = "mlme",
7115};
Johannes Berg55682962007-09-20 13:09:35 -04007116
7117/* multicast groups */
7118static struct genl_multicast_group nl80211_config_mcgrp = {
7119 .name = "config",
7120};
Johannes Berg2a519312009-02-10 21:25:55 +01007121static struct genl_multicast_group nl80211_scan_mcgrp = {
7122 .name = "scan",
7123};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007124static struct genl_multicast_group nl80211_regulatory_mcgrp = {
7125 .name = "regulatory",
7126};
Johannes Berg55682962007-09-20 13:09:35 -04007127
7128/* notification functions */
7129
7130void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
7131{
7132 struct sk_buff *msg;
7133
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007134 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04007135 if (!msg)
7136 return;
7137
7138 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
7139 nlmsg_free(msg);
7140 return;
7141 }
7142
Johannes Berg463d0182009-07-14 00:33:35 +02007143 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7144 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04007145}
7146
Johannes Berg362a4152009-05-24 16:43:15 +02007147static int nl80211_add_scan_req(struct sk_buff *msg,
7148 struct cfg80211_registered_device *rdev)
7149{
7150 struct cfg80211_scan_request *req = rdev->scan_req;
7151 struct nlattr *nest;
7152 int i;
7153
Johannes Berg667503d2009-07-07 03:56:11 +02007154 ASSERT_RDEV_LOCK(rdev);
7155
Johannes Berg362a4152009-05-24 16:43:15 +02007156 if (WARN_ON(!req))
7157 return 0;
7158
7159 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
7160 if (!nest)
7161 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04007162 for (i = 0; i < req->n_ssids; i++) {
7163 if (nla_put(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid))
7164 goto nla_put_failure;
7165 }
Johannes Berg362a4152009-05-24 16:43:15 +02007166 nla_nest_end(msg, nest);
7167
7168 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
7169 if (!nest)
7170 goto nla_put_failure;
David S. Miller9360ffd2012-03-29 04:41:26 -04007171 for (i = 0; i < req->n_channels; i++) {
7172 if (nla_put_u32(msg, i, req->channels[i]->center_freq))
7173 goto nla_put_failure;
7174 }
Johannes Berg362a4152009-05-24 16:43:15 +02007175 nla_nest_end(msg, nest);
7176
David S. Miller9360ffd2012-03-29 04:41:26 -04007177 if (req->ie &&
7178 nla_put(msg, NL80211_ATTR_IE, req->ie_len, req->ie))
7179 goto nla_put_failure;
Johannes Berg362a4152009-05-24 16:43:15 +02007180
7181 return 0;
7182 nla_put_failure:
7183 return -ENOBUFS;
7184}
7185
Johannes Berga538e2d2009-06-16 19:56:42 +02007186static int nl80211_send_scan_msg(struct sk_buff *msg,
7187 struct cfg80211_registered_device *rdev,
7188 struct net_device *netdev,
7189 u32 pid, u32 seq, int flags,
7190 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01007191{
7192 void *hdr;
7193
7194 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7195 if (!hdr)
7196 return -1;
7197
David S. Miller9360ffd2012-03-29 04:41:26 -04007198 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7199 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
7200 goto nla_put_failure;
Johannes Berg2a519312009-02-10 21:25:55 +01007201
Johannes Berg362a4152009-05-24 16:43:15 +02007202 /* ignore errors and send incomplete event anyway */
7203 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01007204
7205 return genlmsg_end(msg, hdr);
7206
7207 nla_put_failure:
7208 genlmsg_cancel(msg, hdr);
7209 return -EMSGSIZE;
7210}
7211
Luciano Coelho807f8a82011-05-11 17:09:35 +03007212static int
7213nl80211_send_sched_scan_msg(struct sk_buff *msg,
7214 struct cfg80211_registered_device *rdev,
7215 struct net_device *netdev,
7216 u32 pid, u32 seq, int flags, u32 cmd)
7217{
7218 void *hdr;
7219
7220 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7221 if (!hdr)
7222 return -1;
7223
David S. Miller9360ffd2012-03-29 04:41:26 -04007224 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7225 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
7226 goto nla_put_failure;
Luciano Coelho807f8a82011-05-11 17:09:35 +03007227
7228 return genlmsg_end(msg, hdr);
7229
7230 nla_put_failure:
7231 genlmsg_cancel(msg, hdr);
7232 return -EMSGSIZE;
7233}
7234
Johannes Berga538e2d2009-06-16 19:56:42 +02007235void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
7236 struct net_device *netdev)
7237{
7238 struct sk_buff *msg;
7239
7240 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7241 if (!msg)
7242 return;
7243
7244 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7245 NL80211_CMD_TRIGGER_SCAN) < 0) {
7246 nlmsg_free(msg);
7247 return;
7248 }
7249
Johannes Berg463d0182009-07-14 00:33:35 +02007250 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7251 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02007252}
7253
Johannes Berg2a519312009-02-10 21:25:55 +01007254void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
7255 struct net_device *netdev)
7256{
7257 struct sk_buff *msg;
7258
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007259 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007260 if (!msg)
7261 return;
7262
Johannes Berga538e2d2009-06-16 19:56:42 +02007263 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7264 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007265 nlmsg_free(msg);
7266 return;
7267 }
7268
Johannes Berg463d0182009-07-14 00:33:35 +02007269 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7270 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007271}
7272
7273void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
7274 struct net_device *netdev)
7275{
7276 struct sk_buff *msg;
7277
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007278 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007279 if (!msg)
7280 return;
7281
Johannes Berga538e2d2009-06-16 19:56:42 +02007282 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7283 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007284 nlmsg_free(msg);
7285 return;
7286 }
7287
Johannes Berg463d0182009-07-14 00:33:35 +02007288 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7289 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007290}
7291
Luciano Coelho807f8a82011-05-11 17:09:35 +03007292void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
7293 struct net_device *netdev)
7294{
7295 struct sk_buff *msg;
7296
7297 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7298 if (!msg)
7299 return;
7300
7301 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
7302 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
7303 nlmsg_free(msg);
7304 return;
7305 }
7306
7307 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7308 nl80211_scan_mcgrp.id, GFP_KERNEL);
7309}
7310
7311void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
7312 struct net_device *netdev, u32 cmd)
7313{
7314 struct sk_buff *msg;
7315
7316 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7317 if (!msg)
7318 return;
7319
7320 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
7321 nlmsg_free(msg);
7322 return;
7323 }
7324
7325 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7326 nl80211_scan_mcgrp.id, GFP_KERNEL);
7327}
7328
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007329/*
7330 * This can happen on global regulatory changes or device specific settings
7331 * based on custom world regulatory domains.
7332 */
7333void nl80211_send_reg_change_event(struct regulatory_request *request)
7334{
7335 struct sk_buff *msg;
7336 void *hdr;
7337
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007338 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007339 if (!msg)
7340 return;
7341
7342 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
7343 if (!hdr) {
7344 nlmsg_free(msg);
7345 return;
7346 }
7347
7348 /* Userspace can always count this one always being set */
David S. Miller9360ffd2012-03-29 04:41:26 -04007349 if (nla_put_u8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator))
7350 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007351
David S. Miller9360ffd2012-03-29 04:41:26 -04007352 if (request->alpha2[0] == '0' && request->alpha2[1] == '0') {
7353 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7354 NL80211_REGDOM_TYPE_WORLD))
7355 goto nla_put_failure;
7356 } else if (request->alpha2[0] == '9' && request->alpha2[1] == '9') {
7357 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7358 NL80211_REGDOM_TYPE_CUSTOM_WORLD))
7359 goto nla_put_failure;
7360 } else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
7361 request->intersect) {
7362 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7363 NL80211_REGDOM_TYPE_INTERSECTION))
7364 goto nla_put_failure;
7365 } else {
7366 if (nla_put_u8(msg, NL80211_ATTR_REG_TYPE,
7367 NL80211_REGDOM_TYPE_COUNTRY) ||
7368 nla_put_string(msg, NL80211_ATTR_REG_ALPHA2,
7369 request->alpha2))
7370 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007371 }
7372
David S. Miller9360ffd2012-03-29 04:41:26 -04007373 if (wiphy_idx_valid(request->wiphy_idx) &&
7374 nla_put_u32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx))
7375 goto nla_put_failure;
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007376
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007377 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007378
Johannes Bergbc43b282009-07-25 10:54:13 +02007379 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02007380 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02007381 GFP_ATOMIC);
7382 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007383
7384 return;
7385
7386nla_put_failure:
7387 genlmsg_cancel(msg, hdr);
7388 nlmsg_free(msg);
7389}
7390
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007391static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
7392 struct net_device *netdev,
7393 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007394 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007395{
7396 struct sk_buff *msg;
7397 void *hdr;
7398
Johannes Berge6d6e342009-07-01 21:26:47 +02007399 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007400 if (!msg)
7401 return;
7402
7403 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7404 if (!hdr) {
7405 nlmsg_free(msg);
7406 return;
7407 }
7408
David S. Miller9360ffd2012-03-29 04:41:26 -04007409 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7410 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7411 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
7412 goto nla_put_failure;
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007413
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007414 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007415
Johannes Berg463d0182009-07-14 00:33:35 +02007416 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7417 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007418 return;
7419
7420 nla_put_failure:
7421 genlmsg_cancel(msg, hdr);
7422 nlmsg_free(msg);
7423}
7424
7425void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007426 struct net_device *netdev, const u8 *buf,
7427 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007428{
7429 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007430 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007431}
7432
7433void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
7434 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007435 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007436{
Johannes Berge6d6e342009-07-01 21:26:47 +02007437 nl80211_send_mlme_event(rdev, netdev, buf, len,
7438 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007439}
7440
Jouni Malinen53b46b82009-03-27 20:53:56 +02007441void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007442 struct net_device *netdev, const u8 *buf,
7443 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007444{
7445 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007446 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007447}
7448
Jouni Malinen53b46b82009-03-27 20:53:56 +02007449void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
7450 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007451 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007452{
7453 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007454 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007455}
7456
Jouni Malinencf4e5942010-12-16 00:52:40 +02007457void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
7458 struct net_device *netdev, const u8 *buf,
7459 size_t len, gfp_t gfp)
7460{
7461 nl80211_send_mlme_event(rdev, netdev, buf, len,
7462 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
7463}
7464
7465void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
7466 struct net_device *netdev, const u8 *buf,
7467 size_t len, gfp_t gfp)
7468{
7469 nl80211_send_mlme_event(rdev, netdev, buf, len,
7470 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
7471}
7472
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04007473static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
7474 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02007475 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007476{
7477 struct sk_buff *msg;
7478 void *hdr;
7479
Johannes Berge6d6e342009-07-01 21:26:47 +02007480 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007481 if (!msg)
7482 return;
7483
7484 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7485 if (!hdr) {
7486 nlmsg_free(msg);
7487 return;
7488 }
7489
David S. Miller9360ffd2012-03-29 04:41:26 -04007490 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7491 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7492 nla_put_flag(msg, NL80211_ATTR_TIMED_OUT) ||
7493 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
7494 goto nla_put_failure;
Jouni Malinen1965c852009-04-22 21:38:25 +03007495
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007496 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03007497
Johannes Berg463d0182009-07-14 00:33:35 +02007498 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7499 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007500 return;
7501
7502 nla_put_failure:
7503 genlmsg_cancel(msg, hdr);
7504 nlmsg_free(msg);
7505}
7506
7507void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007508 struct net_device *netdev, const u8 *addr,
7509 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007510{
7511 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02007512 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007513}
7514
7515void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007516 struct net_device *netdev, const u8 *addr,
7517 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007518{
Johannes Berge6d6e342009-07-01 21:26:47 +02007519 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
7520 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007521}
7522
Samuel Ortizb23aa672009-07-01 21:26:54 +02007523void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
7524 struct net_device *netdev, const u8 *bssid,
7525 const u8 *req_ie, size_t req_ie_len,
7526 const u8 *resp_ie, size_t resp_ie_len,
7527 u16 status, gfp_t gfp)
7528{
7529 struct sk_buff *msg;
7530 void *hdr;
7531
7532 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7533 if (!msg)
7534 return;
7535
7536 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
7537 if (!hdr) {
7538 nlmsg_free(msg);
7539 return;
7540 }
7541
David S. Miller9360ffd2012-03-29 04:41:26 -04007542 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7543 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7544 (bssid && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid)) ||
7545 nla_put_u16(msg, NL80211_ATTR_STATUS_CODE, status) ||
7546 (req_ie &&
7547 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
7548 (resp_ie &&
7549 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
7550 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02007551
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007552 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007553
Johannes Berg463d0182009-07-14 00:33:35 +02007554 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7555 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007556 return;
7557
7558 nla_put_failure:
7559 genlmsg_cancel(msg, hdr);
7560 nlmsg_free(msg);
7561
7562}
7563
7564void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
7565 struct net_device *netdev, const u8 *bssid,
7566 const u8 *req_ie, size_t req_ie_len,
7567 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
7568{
7569 struct sk_buff *msg;
7570 void *hdr;
7571
7572 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7573 if (!msg)
7574 return;
7575
7576 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
7577 if (!hdr) {
7578 nlmsg_free(msg);
7579 return;
7580 }
7581
David S. Miller9360ffd2012-03-29 04:41:26 -04007582 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7583 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7584 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid) ||
7585 (req_ie &&
7586 nla_put(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie)) ||
7587 (resp_ie &&
7588 nla_put(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie)))
7589 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02007590
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007591 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007592
Johannes Berg463d0182009-07-14 00:33:35 +02007593 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7594 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007595 return;
7596
7597 nla_put_failure:
7598 genlmsg_cancel(msg, hdr);
7599 nlmsg_free(msg);
7600
7601}
7602
7603void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
7604 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02007605 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02007606{
7607 struct sk_buff *msg;
7608 void *hdr;
7609
Johannes Berg667503d2009-07-07 03:56:11 +02007610 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007611 if (!msg)
7612 return;
7613
7614 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
7615 if (!hdr) {
7616 nlmsg_free(msg);
7617 return;
7618 }
7619
David S. Miller9360ffd2012-03-29 04:41:26 -04007620 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7621 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7622 (from_ap && reason &&
7623 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason)) ||
7624 (from_ap &&
7625 nla_put_flag(msg, NL80211_ATTR_DISCONNECTED_BY_AP)) ||
7626 (ie && nla_put(msg, NL80211_ATTR_IE, ie_len, ie)))
7627 goto nla_put_failure;
Samuel Ortizb23aa672009-07-01 21:26:54 +02007628
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007629 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007630
Johannes Berg463d0182009-07-14 00:33:35 +02007631 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7632 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007633 return;
7634
7635 nla_put_failure:
7636 genlmsg_cancel(msg, hdr);
7637 nlmsg_free(msg);
7638
7639}
7640
Johannes Berg04a773a2009-04-19 21:24:32 +02007641void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
7642 struct net_device *netdev, const u8 *bssid,
7643 gfp_t gfp)
7644{
7645 struct sk_buff *msg;
7646 void *hdr;
7647
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007648 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007649 if (!msg)
7650 return;
7651
7652 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
7653 if (!hdr) {
7654 nlmsg_free(msg);
7655 return;
7656 }
7657
David S. Miller9360ffd2012-03-29 04:41:26 -04007658 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7659 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7660 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
7661 goto nla_put_failure;
Johannes Berg04a773a2009-04-19 21:24:32 +02007662
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007663 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02007664
Johannes Berg463d0182009-07-14 00:33:35 +02007665 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7666 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007667 return;
7668
7669 nla_put_failure:
7670 genlmsg_cancel(msg, hdr);
7671 nlmsg_free(msg);
7672}
7673
Javier Cardonac93b5e72011-04-07 15:08:34 -07007674void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
7675 struct net_device *netdev,
7676 const u8 *macaddr, const u8* ie, u8 ie_len,
7677 gfp_t gfp)
7678{
7679 struct sk_buff *msg;
7680 void *hdr;
7681
7682 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7683 if (!msg)
7684 return;
7685
7686 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
7687 if (!hdr) {
7688 nlmsg_free(msg);
7689 return;
7690 }
7691
David S. Miller9360ffd2012-03-29 04:41:26 -04007692 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7693 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7694 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr) ||
7695 (ie_len && ie &&
7696 nla_put(msg, NL80211_ATTR_IE, ie_len , ie)))
7697 goto nla_put_failure;
Javier Cardonac93b5e72011-04-07 15:08:34 -07007698
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007699 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07007700
7701 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7702 nl80211_mlme_mcgrp.id, gfp);
7703 return;
7704
7705 nla_put_failure:
7706 genlmsg_cancel(msg, hdr);
7707 nlmsg_free(msg);
7708}
7709
Jouni Malinena3b8b052009-03-27 21:59:49 +02007710void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
7711 struct net_device *netdev, const u8 *addr,
7712 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02007713 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02007714{
7715 struct sk_buff *msg;
7716 void *hdr;
7717
Johannes Berge6d6e342009-07-01 21:26:47 +02007718 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007719 if (!msg)
7720 return;
7721
7722 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
7723 if (!hdr) {
7724 nlmsg_free(msg);
7725 return;
7726 }
7727
David S. Miller9360ffd2012-03-29 04:41:26 -04007728 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7729 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7730 (addr && nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr)) ||
7731 nla_put_u32(msg, NL80211_ATTR_KEY_TYPE, key_type) ||
7732 (key_id != -1 &&
7733 nla_put_u8(msg, NL80211_ATTR_KEY_IDX, key_id)) ||
7734 (tsc && nla_put(msg, NL80211_ATTR_KEY_SEQ, 6, tsc)))
7735 goto nla_put_failure;
Jouni Malinena3b8b052009-03-27 21:59:49 +02007736
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007737 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007738
Johannes Berg463d0182009-07-14 00:33:35 +02007739 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7740 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007741 return;
7742
7743 nla_put_failure:
7744 genlmsg_cancel(msg, hdr);
7745 nlmsg_free(msg);
7746}
7747
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007748void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
7749 struct ieee80211_channel *channel_before,
7750 struct ieee80211_channel *channel_after)
7751{
7752 struct sk_buff *msg;
7753 void *hdr;
7754 struct nlattr *nl_freq;
7755
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007756 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007757 if (!msg)
7758 return;
7759
7760 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
7761 if (!hdr) {
7762 nlmsg_free(msg);
7763 return;
7764 }
7765
7766 /*
7767 * Since we are applying the beacon hint to a wiphy we know its
7768 * wiphy_idx is valid
7769 */
David S. Miller9360ffd2012-03-29 04:41:26 -04007770 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy)))
7771 goto nla_put_failure;
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007772
7773 /* Before */
7774 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
7775 if (!nl_freq)
7776 goto nla_put_failure;
7777 if (nl80211_msg_put_channel(msg, channel_before))
7778 goto nla_put_failure;
7779 nla_nest_end(msg, nl_freq);
7780
7781 /* After */
7782 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
7783 if (!nl_freq)
7784 goto nla_put_failure;
7785 if (nl80211_msg_put_channel(msg, channel_after))
7786 goto nla_put_failure;
7787 nla_nest_end(msg, nl_freq);
7788
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007789 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007790
Johannes Berg463d0182009-07-14 00:33:35 +02007791 rcu_read_lock();
7792 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
7793 GFP_ATOMIC);
7794 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007795
7796 return;
7797
7798nla_put_failure:
7799 genlmsg_cancel(msg, hdr);
7800 nlmsg_free(msg);
7801}
7802
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007803static void nl80211_send_remain_on_chan_event(
7804 int cmd, struct cfg80211_registered_device *rdev,
7805 struct net_device *netdev, u64 cookie,
7806 struct ieee80211_channel *chan,
7807 enum nl80211_channel_type channel_type,
7808 unsigned int duration, gfp_t gfp)
7809{
7810 struct sk_buff *msg;
7811 void *hdr;
7812
7813 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7814 if (!msg)
7815 return;
7816
7817 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7818 if (!hdr) {
7819 nlmsg_free(msg);
7820 return;
7821 }
7822
David S. Miller9360ffd2012-03-29 04:41:26 -04007823 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7824 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7825 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq) ||
7826 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type) ||
7827 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie))
7828 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007829
David S. Miller9360ffd2012-03-29 04:41:26 -04007830 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL &&
7831 nla_put_u32(msg, NL80211_ATTR_DURATION, duration))
7832 goto nla_put_failure;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007833
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007834 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007835
7836 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7837 nl80211_mlme_mcgrp.id, gfp);
7838 return;
7839
7840 nla_put_failure:
7841 genlmsg_cancel(msg, hdr);
7842 nlmsg_free(msg);
7843}
7844
7845void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
7846 struct net_device *netdev, u64 cookie,
7847 struct ieee80211_channel *chan,
7848 enum nl80211_channel_type channel_type,
7849 unsigned int duration, gfp_t gfp)
7850{
7851 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
7852 rdev, netdev, cookie, chan,
7853 channel_type, duration, gfp);
7854}
7855
7856void nl80211_send_remain_on_channel_cancel(
7857 struct cfg80211_registered_device *rdev, struct net_device *netdev,
7858 u64 cookie, struct ieee80211_channel *chan,
7859 enum nl80211_channel_type channel_type, gfp_t gfp)
7860{
7861 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7862 rdev, netdev, cookie, chan,
7863 channel_type, 0, gfp);
7864}
7865
Johannes Berg98b62182009-12-23 13:15:44 +01007866void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
7867 struct net_device *dev, const u8 *mac_addr,
7868 struct station_info *sinfo, gfp_t gfp)
7869{
7870 struct sk_buff *msg;
7871
7872 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7873 if (!msg)
7874 return;
7875
John W. Linville66266b32012-03-15 13:25:41 -04007876 if (nl80211_send_station(msg, 0, 0, 0,
7877 rdev, dev, mac_addr, sinfo) < 0) {
Johannes Berg98b62182009-12-23 13:15:44 +01007878 nlmsg_free(msg);
7879 return;
7880 }
7881
7882 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7883 nl80211_mlme_mcgrp.id, gfp);
7884}
7885
Jouni Malinenec15e682011-03-23 15:29:52 +02007886void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
7887 struct net_device *dev, const u8 *mac_addr,
7888 gfp_t gfp)
7889{
7890 struct sk_buff *msg;
7891 void *hdr;
7892
7893 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7894 if (!msg)
7895 return;
7896
7897 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
7898 if (!hdr) {
7899 nlmsg_free(msg);
7900 return;
7901 }
7902
David S. Miller9360ffd2012-03-29 04:41:26 -04007903 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
7904 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr))
7905 goto nla_put_failure;
Jouni Malinenec15e682011-03-23 15:29:52 +02007906
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007907 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02007908
7909 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7910 nl80211_mlme_mcgrp.id, gfp);
7911 return;
7912
7913 nla_put_failure:
7914 genlmsg_cancel(msg, hdr);
7915 nlmsg_free(msg);
7916}
7917
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007918static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
7919 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01007920{
7921 struct wireless_dev *wdev = dev->ieee80211_ptr;
7922 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7923 struct sk_buff *msg;
7924 void *hdr;
7925 int err;
7926 u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
7927
7928 if (!nlpid)
7929 return false;
7930
7931 msg = nlmsg_new(100, gfp);
7932 if (!msg)
7933 return true;
7934
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007935 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01007936 if (!hdr) {
7937 nlmsg_free(msg);
7938 return true;
7939 }
7940
David S. Miller9360ffd2012-03-29 04:41:26 -04007941 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7942 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
7943 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr))
7944 goto nla_put_failure;
Johannes Berg28946da2011-11-04 11:18:12 +01007945
7946 err = genlmsg_end(msg, hdr);
7947 if (err < 0) {
7948 nlmsg_free(msg);
7949 return true;
7950 }
7951
7952 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7953 return true;
7954
7955 nla_put_failure:
7956 genlmsg_cancel(msg, hdr);
7957 nlmsg_free(msg);
7958 return true;
7959}
7960
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007961bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
7962{
7963 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
7964 addr, gfp);
7965}
7966
7967bool nl80211_unexpected_4addr_frame(struct net_device *dev,
7968 const u8 *addr, gfp_t gfp)
7969{
7970 return __nl80211_unexpected_frame(dev,
7971 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
7972 addr, gfp);
7973}
7974
Johannes Berg2e161f782010-08-12 15:38:38 +02007975int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
7976 struct net_device *netdev, u32 nlpid,
Johannes Berg804483e2012-03-05 22:18:41 +01007977 int freq, int sig_dbm,
7978 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007979{
7980 struct sk_buff *msg;
7981 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02007982
7983 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7984 if (!msg)
7985 return -ENOMEM;
7986
Johannes Berg2e161f782010-08-12 15:38:38 +02007987 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02007988 if (!hdr) {
7989 nlmsg_free(msg);
7990 return -ENOMEM;
7991 }
7992
David S. Miller9360ffd2012-03-29 04:41:26 -04007993 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
7994 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
7995 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
7996 (sig_dbm &&
7997 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
7998 nla_put(msg, NL80211_ATTR_FRAME, len, buf))
7999 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02008000
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008001 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02008002
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008003 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
Jouni Malinen026331c2010-02-15 12:53:10 +02008004
8005 nla_put_failure:
8006 genlmsg_cancel(msg, hdr);
8007 nlmsg_free(msg);
8008 return -ENOBUFS;
8009}
8010
Johannes Berg2e161f782010-08-12 15:38:38 +02008011void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
8012 struct net_device *netdev, u64 cookie,
8013 const u8 *buf, size_t len, bool ack,
8014 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02008015{
8016 struct sk_buff *msg;
8017 void *hdr;
8018
8019 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
8020 if (!msg)
8021 return;
8022
Johannes Berg2e161f782010-08-12 15:38:38 +02008023 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02008024 if (!hdr) {
8025 nlmsg_free(msg);
8026 return;
8027 }
8028
David S. Miller9360ffd2012-03-29 04:41:26 -04008029 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8030 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8031 nla_put(msg, NL80211_ATTR_FRAME, len, buf) ||
8032 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
8033 (ack && nla_put_flag(msg, NL80211_ATTR_ACK)))
8034 goto nla_put_failure;
Jouni Malinen026331c2010-02-15 12:53:10 +02008035
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008036 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02008037
8038 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
8039 return;
8040
8041 nla_put_failure:
8042 genlmsg_cancel(msg, hdr);
8043 nlmsg_free(msg);
8044}
8045
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008046void
8047nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
8048 struct net_device *netdev,
8049 enum nl80211_cqm_rssi_threshold_event rssi_event,
8050 gfp_t gfp)
8051{
8052 struct sk_buff *msg;
8053 struct nlattr *pinfoattr;
8054 void *hdr;
8055
8056 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8057 if (!msg)
8058 return;
8059
8060 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
8061 if (!hdr) {
8062 nlmsg_free(msg);
8063 return;
8064 }
8065
David S. Miller9360ffd2012-03-29 04:41:26 -04008066 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8067 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
8068 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008069
8070 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
8071 if (!pinfoattr)
8072 goto nla_put_failure;
8073
David S. Miller9360ffd2012-03-29 04:41:26 -04008074 if (nla_put_u32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
8075 rssi_event))
8076 goto nla_put_failure;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008077
8078 nla_nest_end(msg, pinfoattr);
8079
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008080 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008081
8082 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8083 nl80211_mlme_mcgrp.id, gfp);
8084 return;
8085
8086 nla_put_failure:
8087 genlmsg_cancel(msg, hdr);
8088 nlmsg_free(msg);
8089}
8090
Johannes Berge5497d72011-07-05 16:35:40 +02008091void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
8092 struct net_device *netdev, const u8 *bssid,
8093 const u8 *replay_ctr, gfp_t gfp)
8094{
8095 struct sk_buff *msg;
8096 struct nlattr *rekey_attr;
8097 void *hdr;
8098
8099 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8100 if (!msg)
8101 return;
8102
8103 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
8104 if (!hdr) {
8105 nlmsg_free(msg);
8106 return;
8107 }
8108
David S. Miller9360ffd2012-03-29 04:41:26 -04008109 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8110 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8111 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid))
8112 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +02008113
8114 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
8115 if (!rekey_attr)
8116 goto nla_put_failure;
8117
David S. Miller9360ffd2012-03-29 04:41:26 -04008118 if (nla_put(msg, NL80211_REKEY_DATA_REPLAY_CTR,
8119 NL80211_REPLAY_CTR_LEN, replay_ctr))
8120 goto nla_put_failure;
Johannes Berge5497d72011-07-05 16:35:40 +02008121
8122 nla_nest_end(msg, rekey_attr);
8123
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008124 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02008125
8126 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8127 nl80211_mlme_mcgrp.id, gfp);
8128 return;
8129
8130 nla_put_failure:
8131 genlmsg_cancel(msg, hdr);
8132 nlmsg_free(msg);
8133}
8134
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008135void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
8136 struct net_device *netdev, int index,
8137 const u8 *bssid, bool preauth, gfp_t gfp)
8138{
8139 struct sk_buff *msg;
8140 struct nlattr *attr;
8141 void *hdr;
8142
8143 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8144 if (!msg)
8145 return;
8146
8147 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
8148 if (!hdr) {
8149 nlmsg_free(msg);
8150 return;
8151 }
8152
David S. Miller9360ffd2012-03-29 04:41:26 -04008153 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8154 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex))
8155 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008156
8157 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
8158 if (!attr)
8159 goto nla_put_failure;
8160
David S. Miller9360ffd2012-03-29 04:41:26 -04008161 if (nla_put_u32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index) ||
8162 nla_put(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid) ||
8163 (preauth &&
8164 nla_put_flag(msg, NL80211_PMKSA_CANDIDATE_PREAUTH)))
8165 goto nla_put_failure;
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008166
8167 nla_nest_end(msg, attr);
8168
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008169 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008170
8171 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8172 nl80211_mlme_mcgrp.id, gfp);
8173 return;
8174
8175 nla_put_failure:
8176 genlmsg_cancel(msg, hdr);
8177 nlmsg_free(msg);
8178}
8179
Thomas Pedersen53145262012-04-06 13:35:47 -07008180void nl80211_ch_switch_notify(struct cfg80211_registered_device *rdev,
8181 struct net_device *netdev, int freq,
8182 enum nl80211_channel_type type, gfp_t gfp)
8183{
8184 struct sk_buff *msg;
8185 void *hdr;
8186
8187 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8188 if (!msg)
8189 return;
8190
8191 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CH_SWITCH_NOTIFY);
8192 if (!hdr) {
8193 nlmsg_free(msg);
8194 return;
8195 }
8196
John W. Linville7eab0f62012-04-12 14:25:14 -04008197 if (nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8198 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq) ||
8199 nla_put_u32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, type))
8200 goto nla_put_failure;
Thomas Pedersen53145262012-04-06 13:35:47 -07008201
8202 genlmsg_end(msg, hdr);
8203
8204 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8205 nl80211_mlme_mcgrp.id, gfp);
8206 return;
8207
8208 nla_put_failure:
8209 genlmsg_cancel(msg, hdr);
8210 nlmsg_free(msg);
8211}
8212
Johannes Bergc063dbf2010-11-24 08:10:05 +01008213void
8214nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
8215 struct net_device *netdev, const u8 *peer,
8216 u32 num_packets, gfp_t gfp)
8217{
8218 struct sk_buff *msg;
8219 struct nlattr *pinfoattr;
8220 void *hdr;
8221
8222 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8223 if (!msg)
8224 return;
8225
8226 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
8227 if (!hdr) {
8228 nlmsg_free(msg);
8229 return;
8230 }
8231
David S. Miller9360ffd2012-03-29 04:41:26 -04008232 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8233 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex) ||
8234 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer))
8235 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +01008236
8237 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
8238 if (!pinfoattr)
8239 goto nla_put_failure;
8240
David S. Miller9360ffd2012-03-29 04:41:26 -04008241 if (nla_put_u32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets))
8242 goto nla_put_failure;
Johannes Bergc063dbf2010-11-24 08:10:05 +01008243
8244 nla_nest_end(msg, pinfoattr);
8245
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008246 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01008247
8248 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8249 nl80211_mlme_mcgrp.id, gfp);
8250 return;
8251
8252 nla_put_failure:
8253 genlmsg_cancel(msg, hdr);
8254 nlmsg_free(msg);
8255}
8256
Johannes Berg7f6cf312011-11-04 11:18:15 +01008257void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
8258 u64 cookie, bool acked, gfp_t gfp)
8259{
8260 struct wireless_dev *wdev = dev->ieee80211_ptr;
8261 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
8262 struct sk_buff *msg;
8263 void *hdr;
8264 int err;
8265
8266 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8267 if (!msg)
8268 return;
8269
8270 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
8271 if (!hdr) {
8272 nlmsg_free(msg);
8273 return;
8274 }
8275
David S. Miller9360ffd2012-03-29 04:41:26 -04008276 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8277 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
8278 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, addr) ||
8279 nla_put_u64(msg, NL80211_ATTR_COOKIE, cookie) ||
8280 (acked && nla_put_flag(msg, NL80211_ATTR_ACK)))
8281 goto nla_put_failure;
Johannes Berg7f6cf312011-11-04 11:18:15 +01008282
8283 err = genlmsg_end(msg, hdr);
8284 if (err < 0) {
8285 nlmsg_free(msg);
8286 return;
8287 }
8288
8289 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8290 nl80211_mlme_mcgrp.id, gfp);
8291 return;
8292
8293 nla_put_failure:
8294 genlmsg_cancel(msg, hdr);
8295 nlmsg_free(msg);
8296}
8297EXPORT_SYMBOL(cfg80211_probe_status);
8298
Johannes Berg5e760232011-11-04 11:18:17 +01008299void cfg80211_report_obss_beacon(struct wiphy *wiphy,
8300 const u8 *frame, size_t len,
Johannes Berg804483e2012-03-05 22:18:41 +01008301 int freq, int sig_dbm, gfp_t gfp)
Johannes Berg5e760232011-11-04 11:18:17 +01008302{
8303 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
8304 struct sk_buff *msg;
8305 void *hdr;
8306 u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
8307
8308 if (!nlpid)
8309 return;
8310
8311 msg = nlmsg_new(len + 100, gfp);
8312 if (!msg)
8313 return;
8314
8315 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
8316 if (!hdr) {
8317 nlmsg_free(msg);
8318 return;
8319 }
8320
David S. Miller9360ffd2012-03-29 04:41:26 -04008321 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8322 (freq &&
8323 nla_put_u32(msg, NL80211_ATTR_WIPHY_FREQ, freq)) ||
8324 (sig_dbm &&
8325 nla_put_u32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm)) ||
8326 nla_put(msg, NL80211_ATTR_FRAME, len, frame))
8327 goto nla_put_failure;
Johannes Berg5e760232011-11-04 11:18:17 +01008328
8329 genlmsg_end(msg, hdr);
8330
8331 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
8332 return;
8333
8334 nla_put_failure:
8335 genlmsg_cancel(msg, hdr);
8336 nlmsg_free(msg);
8337}
8338EXPORT_SYMBOL(cfg80211_report_obss_beacon);
8339
Jouni Malinen026331c2010-02-15 12:53:10 +02008340static int nl80211_netlink_notify(struct notifier_block * nb,
8341 unsigned long state,
8342 void *_notify)
8343{
8344 struct netlink_notify *notify = _notify;
8345 struct cfg80211_registered_device *rdev;
8346 struct wireless_dev *wdev;
8347
8348 if (state != NETLINK_URELEASE)
8349 return NOTIFY_DONE;
8350
8351 rcu_read_lock();
8352
Johannes Berg5e760232011-11-04 11:18:17 +01008353 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +02008354 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f782010-08-12 15:38:38 +02008355 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Johannes Berg5e760232011-11-04 11:18:17 +01008356 if (rdev->ap_beacons_nlpid == notify->pid)
8357 rdev->ap_beacons_nlpid = 0;
8358 }
Jouni Malinen026331c2010-02-15 12:53:10 +02008359
8360 rcu_read_unlock();
8361
8362 return NOTIFY_DONE;
8363}
8364
8365static struct notifier_block nl80211_netlink_notifier = {
8366 .notifier_call = nl80211_netlink_notify,
8367};
8368
Johannes Berg55682962007-09-20 13:09:35 -04008369/* initialisation/exit functions */
8370
8371int nl80211_init(void)
8372{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008373 int err;
Johannes Berg55682962007-09-20 13:09:35 -04008374
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008375 err = genl_register_family_with_ops(&nl80211_fam,
8376 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04008377 if (err)
8378 return err;
8379
Johannes Berg55682962007-09-20 13:09:35 -04008380 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
8381 if (err)
8382 goto err_out;
8383
Johannes Berg2a519312009-02-10 21:25:55 +01008384 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
8385 if (err)
8386 goto err_out;
8387
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008388 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
8389 if (err)
8390 goto err_out;
8391
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008392 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
8393 if (err)
8394 goto err_out;
8395
Johannes Bergaff89a92009-07-01 21:26:51 +02008396#ifdef CONFIG_NL80211_TESTMODE
8397 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
8398 if (err)
8399 goto err_out;
8400#endif
8401
Jouni Malinen026331c2010-02-15 12:53:10 +02008402 err = netlink_register_notifier(&nl80211_netlink_notifier);
8403 if (err)
8404 goto err_out;
8405
Johannes Berg55682962007-09-20 13:09:35 -04008406 return 0;
8407 err_out:
8408 genl_unregister_family(&nl80211_fam);
8409 return err;
8410}
8411
8412void nl80211_exit(void)
8413{
Jouni Malinen026331c2010-02-15 12:53:10 +02008414 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04008415 genl_unregister_family(&nl80211_fam);
8416}