blob: e1fa62e584da67ba4566c23808bed455008f1616 [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,
118 .len = IEEE80211_MAX_MESH_ID_LEN },
119 [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 Malinendc6382c2009-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 Berg2e161f72010-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 },
Mahesh Palivelacf7f78d2012-10-11 08:04:52 +0000209 [NL80211_ATTR_WDEV] = { .type = NLA_U64 },
210 [NL80211_ATTR_USER_REG_HINT_TYPE] = { .type = NLA_U32 },
211 [NL80211_ATTR_SAE_DATA] = { .type = NLA_BINARY, },
212 [NL80211_ATTR_VHT_CAPABILITY] = { .len = NL80211_VHT_CAPABILITY_LEN },
Jouni Malinen1400ced2013-02-14 19:10:13 +0000213 [NL80211_ATTR_STA_CAPABILITY] = { .type = NLA_U16 },
214 [NL80211_ATTR_STA_EXT_CAPABILITY] = { .type = NLA_BINARY, },
Jouni Malinen2b4303f2013-03-19 14:30:49 +0530215 [NL80211_ATTR_SPLIT_WIPHY_DUMP] = { .type = NLA_FLAG, },
216 [NL80211_ATTR_DISABLE_VHT] = { .type = NLA_FLAG },
217 [NL80211_ATTR_VHT_CAPABILITY_MASK] = {
218 .len = NL80211_VHT_CAPABILITY_LEN,
219 },
220 [NL80211_ATTR_MDID] = { .type = NLA_U16 },
221 [NL80211_ATTR_IE_RIC] = { .type = NLA_BINARY,
222 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg55682962007-09-20 13:09:35 -0400223};
224
Johannes Berge31b8212010-10-05 19:39:30 +0200225/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000226static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200227 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200228 [NL80211_KEY_IDX] = { .type = NLA_U8 },
229 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200230 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200231 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
232 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200233 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100234 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
235};
236
237/* policy for the key default flags */
238static const struct nla_policy
239nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
240 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
241 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200242};
243
Johannes Bergff1b6e62011-05-04 15:37:28 +0200244/* policy for WoWLAN attributes */
245static const struct nla_policy
246nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
247 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
248 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
249 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
250 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200251 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
252 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
253 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
254 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200255};
256
Johannes Berge5497d72011-07-05 16:35:40 +0200257/* policy for GTK rekey offload attributes */
258static const struct nla_policy
259nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
260 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
261 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
262 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
263};
264
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300265static const struct nla_policy
266nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
267 [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY,
268 .len = IEEE80211_MAX_SSID_LEN },
269};
270
Holger Schuriga0438972009-11-11 11:30:02 +0100271/* ifidx get helper */
272static int nl80211_get_ifidx(struct netlink_callback *cb)
273{
274 int res;
275
276 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
277 nl80211_fam.attrbuf, nl80211_fam.maxattr,
278 nl80211_policy);
279 if (res)
280 return res;
281
282 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
283 return -EINVAL;
284
285 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
286 if (!res)
287 return -EINVAL;
288 return res;
289}
290
Johannes Berg67748892010-10-04 21:14:06 +0200291static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
292 struct netlink_callback *cb,
293 struct cfg80211_registered_device **rdev,
294 struct net_device **dev)
295{
296 int ifidx = cb->args[0];
297 int err;
298
299 if (!ifidx)
300 ifidx = nl80211_get_ifidx(cb);
301 if (ifidx < 0)
302 return ifidx;
303
304 cb->args[0] = ifidx;
305
306 rtnl_lock();
307
308 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
309 if (!*dev) {
310 err = -ENODEV;
311 goto out_rtnl;
312 }
313
314 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100315 if (IS_ERR(*rdev)) {
316 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200317 goto out_rtnl;
318 }
319
320 return 0;
321 out_rtnl:
322 rtnl_unlock();
323 return err;
324}
325
326static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
327{
328 cfg80211_unlock_rdev(rdev);
329 rtnl_unlock();
330}
331
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100332/* IE validation */
333static bool is_valid_ie_attr(const struct nlattr *attr)
334{
335 const u8 *pos;
336 int len;
337
338 if (!attr)
339 return true;
340
341 pos = nla_data(attr);
342 len = nla_len(attr);
343
344 while (len) {
345 u8 elemlen;
346
347 if (len < 2)
348 return false;
349 len -= 2;
350
351 elemlen = pos[1];
352 if (elemlen > len)
353 return false;
354
355 len -= elemlen;
356 pos += 2 + elemlen;
357 }
358
359 return true;
360}
361
Johannes Berg55682962007-09-20 13:09:35 -0400362/* message building helper */
363static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
364 int flags, u8 cmd)
365{
366 /* since there is no private header just add the generic one */
367 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
368}
369
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400370static int nl80211_msg_put_channel(struct sk_buff *msg,
371 struct ieee80211_channel *chan)
372{
373 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
374 chan->center_freq);
375
376 if (chan->flags & IEEE80211_CHAN_DISABLED)
377 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
378 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
379 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
380 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
381 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
382 if (chan->flags & IEEE80211_CHAN_RADAR)
383 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
384
385 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
386 DBM_TO_MBM(chan->max_power));
387
388 return 0;
389
390 nla_put_failure:
391 return -ENOBUFS;
392}
393
Johannes Berg55682962007-09-20 13:09:35 -0400394/* netlink command implementations */
395
Johannes Bergb9454e82009-07-08 13:29:08 +0200396struct key_parse {
397 struct key_params p;
398 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200399 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200400 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100401 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200402};
403
404static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
405{
406 struct nlattr *tb[NL80211_KEY_MAX + 1];
407 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
408 nl80211_key_policy);
409 if (err)
410 return err;
411
412 k->def = !!tb[NL80211_KEY_DEFAULT];
413 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
414
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100415 if (k->def) {
416 k->def_uni = true;
417 k->def_multi = true;
418 }
419 if (k->defmgmt)
420 k->def_multi = true;
421
Johannes Bergb9454e82009-07-08 13:29:08 +0200422 if (tb[NL80211_KEY_IDX])
423 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
424
425 if (tb[NL80211_KEY_DATA]) {
426 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
427 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
428 }
429
430 if (tb[NL80211_KEY_SEQ]) {
431 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
432 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
433 }
434
435 if (tb[NL80211_KEY_CIPHER])
436 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
437
Johannes Berge31b8212010-10-05 19:39:30 +0200438 if (tb[NL80211_KEY_TYPE]) {
439 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
440 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
441 return -EINVAL;
442 }
443
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100444 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
445 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
Johannes Berg2da8f412012-01-20 13:52:37 +0100446 err = nla_parse_nested(kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
447 tb[NL80211_KEY_DEFAULT_TYPES],
448 nl80211_key_default_policy);
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100449 if (err)
450 return err;
451
452 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
453 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
454 }
455
Johannes Bergb9454e82009-07-08 13:29:08 +0200456 return 0;
457}
458
459static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
460{
461 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
462 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
463 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
464 }
465
466 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
467 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
468 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
469 }
470
471 if (info->attrs[NL80211_ATTR_KEY_IDX])
472 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
473
474 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
475 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
476
477 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
478 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
479
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100480 if (k->def) {
481 k->def_uni = true;
482 k->def_multi = true;
483 }
484 if (k->defmgmt)
485 k->def_multi = true;
486
Johannes Berge31b8212010-10-05 19:39:30 +0200487 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
488 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
489 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
490 return -EINVAL;
491 }
492
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100493 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
494 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
495 int err = nla_parse_nested(
496 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
497 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
498 nl80211_key_default_policy);
499 if (err)
500 return err;
501
502 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
503 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
504 }
505
Johannes Bergb9454e82009-07-08 13:29:08 +0200506 return 0;
507}
508
509static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
510{
511 int err;
512
513 memset(k, 0, sizeof(*k));
514 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200515 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200516
517 if (info->attrs[NL80211_ATTR_KEY])
518 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
519 else
520 err = nl80211_parse_key_old(info, k);
521
522 if (err)
523 return err;
524
525 if (k->def && k->defmgmt)
526 return -EINVAL;
527
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100528 if (k->defmgmt) {
529 if (k->def_uni || !k->def_multi)
530 return -EINVAL;
531 }
532
Johannes Bergb9454e82009-07-08 13:29:08 +0200533 if (k->idx != -1) {
534 if (k->defmgmt) {
535 if (k->idx < 4 || k->idx > 5)
536 return -EINVAL;
537 } else if (k->def) {
538 if (k->idx < 0 || k->idx > 3)
539 return -EINVAL;
540 } else {
541 if (k->idx < 0 || k->idx > 5)
542 return -EINVAL;
543 }
544 }
545
546 return 0;
547}
548
Johannes Bergfffd0932009-07-08 14:22:54 +0200549static struct cfg80211_cached_keys *
550nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
551 struct nlattr *keys)
552{
553 struct key_parse parse;
554 struct nlattr *key;
555 struct cfg80211_cached_keys *result;
556 int rem, err, def = 0;
557
558 result = kzalloc(sizeof(*result), GFP_KERNEL);
559 if (!result)
560 return ERR_PTR(-ENOMEM);
561
562 result->def = -1;
563 result->defmgmt = -1;
564
565 nla_for_each_nested(key, keys, rem) {
566 memset(&parse, 0, sizeof(parse));
567 parse.idx = -1;
568
569 err = nl80211_parse_key_new(key, &parse);
570 if (err)
571 goto error;
572 err = -EINVAL;
573 if (!parse.p.key)
574 goto error;
575 if (parse.idx < 0 || parse.idx > 4)
576 goto error;
577 if (parse.def) {
578 if (def)
579 goto error;
580 def = 1;
581 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100582 if (!parse.def_uni || !parse.def_multi)
583 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200584 } else if (parse.defmgmt)
585 goto error;
586 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200587 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200588 if (err)
589 goto error;
590 result->params[parse.idx].cipher = parse.p.cipher;
591 result->params[parse.idx].key_len = parse.p.key_len;
592 result->params[parse.idx].key = result->data[parse.idx];
593 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
594 }
595
596 return result;
597 error:
598 kfree(result);
599 return ERR_PTR(err);
600}
601
602static int nl80211_key_allowed(struct wireless_dev *wdev)
603{
604 ASSERT_WDEV_LOCK(wdev);
605
Johannes Bergfffd0932009-07-08 14:22:54 +0200606 switch (wdev->iftype) {
607 case NL80211_IFTYPE_AP:
608 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200609 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700610 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200611 break;
612 case NL80211_IFTYPE_ADHOC:
613 if (!wdev->current_bss)
614 return -ENOLINK;
615 break;
616 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200617 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200618 if (wdev->sme_state != CFG80211_SME_CONNECTED)
619 return -ENOLINK;
620 break;
621 default:
622 return -EINVAL;
623 }
624
625 return 0;
626}
627
Johannes Berg7527a782011-05-13 10:58:57 +0200628static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
629{
630 struct nlattr *nl_modes = nla_nest_start(msg, attr);
631 int i;
632
633 if (!nl_modes)
634 goto nla_put_failure;
635
636 i = 0;
637 while (ifmodes) {
638 if (ifmodes & 1)
639 NLA_PUT_FLAG(msg, i);
640 ifmodes >>= 1;
641 i++;
642 }
643
644 nla_nest_end(msg, nl_modes);
645 return 0;
646
647nla_put_failure:
648 return -ENOBUFS;
649}
650
651static int nl80211_put_iface_combinations(struct wiphy *wiphy,
652 struct sk_buff *msg)
653{
654 struct nlattr *nl_combis;
655 int i, j;
656
657 nl_combis = nla_nest_start(msg,
658 NL80211_ATTR_INTERFACE_COMBINATIONS);
659 if (!nl_combis)
660 goto nla_put_failure;
661
662 for (i = 0; i < wiphy->n_iface_combinations; i++) {
663 const struct ieee80211_iface_combination *c;
664 struct nlattr *nl_combi, *nl_limits;
665
666 c = &wiphy->iface_combinations[i];
667
668 nl_combi = nla_nest_start(msg, i + 1);
669 if (!nl_combi)
670 goto nla_put_failure;
671
672 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
673 if (!nl_limits)
674 goto nla_put_failure;
675
676 for (j = 0; j < c->n_limits; j++) {
677 struct nlattr *nl_limit;
678
679 nl_limit = nla_nest_start(msg, j + 1);
680 if (!nl_limit)
681 goto nla_put_failure;
682 NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
683 c->limits[j].max);
684 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
685 c->limits[j].types))
686 goto nla_put_failure;
687 nla_nest_end(msg, nl_limit);
688 }
689
690 nla_nest_end(msg, nl_limits);
691
692 if (c->beacon_int_infra_match)
693 NLA_PUT_FLAG(msg,
694 NL80211_IFACE_COMB_STA_AP_BI_MATCH);
695 NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
696 c->num_different_channels);
697 NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
698 c->max_interfaces);
699
700 nla_nest_end(msg, nl_combi);
701 }
702
703 nla_nest_end(msg, nl_combis);
704
705 return 0;
706nla_put_failure:
707 return -ENOBUFS;
708}
709
Johannes Berg55682962007-09-20 13:09:35 -0400710static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
711 struct cfg80211_registered_device *dev)
712{
713 void *hdr;
Johannes Bergee688b02008-01-24 19:38:39 +0100714 struct nlattr *nl_bands, *nl_band;
715 struct nlattr *nl_freqs, *nl_freq;
716 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100717 struct nlattr *nl_cmds;
Johannes Bergee688b02008-01-24 19:38:39 +0100718 enum ieee80211_band band;
719 struct ieee80211_channel *chan;
720 struct ieee80211_rate *rate;
721 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200722 const struct ieee80211_txrx_stypes *mgmt_stypes =
723 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400724
725 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
726 if (!hdr)
727 return -1;
728
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500729 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400730 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200731
Johannes Bergf5ea9122009-08-07 16:17:38 +0200732 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
733 cfg80211_rdev_list_generation);
734
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200735 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
736 dev->wiphy.retry_short);
737 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
738 dev->wiphy.retry_long);
739 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
740 dev->wiphy.frag_threshold);
741 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
742 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100743 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
744 dev->wiphy.coverage_class);
Johannes Berg2a519312009-02-10 21:25:55 +0100745 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
746 dev->wiphy.max_scan_ssids);
Luciano Coelho93b6aa62011-07-13 14:57:28 +0300747 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
748 dev->wiphy.max_sched_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200749 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
750 dev->wiphy.max_scan_ie_len);
Luciano Coelho5a865ba2011-07-13 14:57:29 +0300751 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
752 dev->wiphy.max_sched_scan_ie_len);
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300753 NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS,
754 dev->wiphy.max_match_sets);
Johannes Bergee688b02008-01-24 19:38:39 +0100755
Johannes Berge31b8212010-10-05 19:39:30 +0200756 if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
757 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
Javier Cardona15d5dda2011-04-07 15:08:28 -0700758 if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
759 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
Eliad Pellercedb5412011-08-31 11:29:43 +0300760 if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD)
761 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530762 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)
763 NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300764 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)
765 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT);
766 if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)
767 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530768
Johannes Berg25e47c12009-04-02 20:14:06 +0200769 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
770 sizeof(u32) * dev->wiphy.n_cipher_suites,
771 dev->wiphy.cipher_suites);
772
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100773 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
774 dev->wiphy.max_num_pmkids);
775
Johannes Bergc0692b82010-08-27 14:26:53 +0300776 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
777 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
778
Bruno Randolf39fd5de2010-12-16 11:30:28 +0900779 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
780 dev->wiphy.available_antennas_tx);
781 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
782 dev->wiphy.available_antennas_rx);
783
Arik Nemtsov87bbbe22011-11-10 11:28:55 +0200784 if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD)
785 NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
786 dev->wiphy.probe_resp_offload);
787
Bruno Randolf7f531e02010-12-16 11:30:22 +0900788 if ((dev->wiphy.available_antennas_tx ||
789 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900790 u32 tx_ant = 0, rx_ant = 0;
791 int res;
792 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
793 if (!res) {
794 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
795 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
796 }
797 }
798
Johannes Berg7527a782011-05-13 10:58:57 +0200799 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
800 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700801 goto nla_put_failure;
802
Johannes Bergee688b02008-01-24 19:38:39 +0100803 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
804 if (!nl_bands)
805 goto nla_put_failure;
806
807 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
808 if (!dev->wiphy.bands[band])
809 continue;
810
811 nl_band = nla_nest_start(msg, band);
812 if (!nl_band)
813 goto nla_put_failure;
814
Johannes Bergd51626d2008-10-09 12:20:13 +0200815 /* add HT info */
816 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
817 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
818 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
819 &dev->wiphy.bands[band]->ht_cap.mcs);
820 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
821 dev->wiphy.bands[band]->ht_cap.cap);
822 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
823 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
824 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
825 dev->wiphy.bands[band]->ht_cap.ampdu_density);
826 }
827
Mahesh Palivela04d2e732012-06-22 07:27:46 +0000828 /* add VHT info */
829 if (dev->wiphy.bands[band]->vht_cap.vht_supported &&
830 (nla_put(msg, NL80211_BAND_ATTR_VHT_MCS_SET,
831 sizeof(dev->wiphy.bands[band]->vht_cap.vht_mcs),
832 &dev->wiphy.bands[band]->vht_cap.vht_mcs) ||
833 nla_put_u32(msg, NL80211_BAND_ATTR_VHT_CAPA,
834 dev->wiphy.bands[band]->vht_cap.cap)))
835 goto nla_put_failure;
836
Johannes Bergee688b02008-01-24 19:38:39 +0100837 /* add frequencies */
838 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
839 if (!nl_freqs)
840 goto nla_put_failure;
841
842 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
843 nl_freq = nla_nest_start(msg, i);
844 if (!nl_freq)
845 goto nla_put_failure;
846
847 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b02008-01-24 19:38:39 +0100848
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400849 if (nl80211_msg_put_channel(msg, chan))
850 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200851
Johannes Bergee688b02008-01-24 19:38:39 +0100852 nla_nest_end(msg, nl_freq);
853 }
854
855 nla_nest_end(msg, nl_freqs);
856
857 /* add bitrates */
858 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
859 if (!nl_rates)
860 goto nla_put_failure;
861
862 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
863 nl_rate = nla_nest_start(msg, i);
864 if (!nl_rate)
865 goto nla_put_failure;
866
867 rate = &dev->wiphy.bands[band]->bitrates[i];
868 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
869 rate->bitrate);
870 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
871 NLA_PUT_FLAG(msg,
872 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
873
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++; \
892 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
893 } \
894 } while (0)
895
896 CMD(add_virtual_intf, NEW_INTERFACE);
897 CMD(change_virtual_intf, SET_INTERFACE);
898 CMD(add_key, NEW_KEY);
Johannes Berg88600202012-02-13 15:17:18 +0100899 CMD(start_ap, START_AP);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100900 CMD(add_station, NEW_STATION);
901 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800902 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100903 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200904 CMD(auth, AUTHENTICATE);
905 CMD(assoc, ASSOCIATE);
906 CMD(deauth, DEAUTHENTICATE);
907 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200908 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100909 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100910 CMD(set_pmksa, SET_PMKSA);
911 CMD(del_pmksa, DEL_PMKSA);
912 CMD(flush_pmksa, FLUSH_PMKSA);
Johannes Berg7c4ef712011-11-18 15:33:48 +0100913 if (dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
914 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200915 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200916 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100917 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100918 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200919 i++;
920 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
921 }
Johannes Bergf444de02010-05-05 15:25:02 +0200922 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400923 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300924 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
925 CMD(tdls_mgmt, TDLS_MGMT);
926 CMD(tdls_oper, TDLS_OPER);
927 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300928 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
929 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +0100930 CMD(probe_client, PROBE_CLIENT);
Simon Wunderlich1d9d9212011-11-18 14:20:43 +0100931 CMD(set_noack_map, SET_NOACK_MAP);
Johannes Berg5e760232011-11-04 11:18:17 +0100932 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
933 i++;
934 NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
935 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100936
Kalle Valo4745fc02011-11-17 19:06:10 +0200937#ifdef CONFIG_NL80211_TESTMODE
938 CMD(testmode_cmd, TESTMODE);
939#endif
940
Johannes Berg8fdc6212009-03-14 09:34:01 +0100941#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200942
Johannes Berg6829c872009-07-02 09:13:27 +0200943 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200944 i++;
945 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
946 }
947
Johannes Berg6829c872009-07-02 09:13:27 +0200948 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200949 i++;
950 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
951 }
952
Johannes Berg8fdc6212009-03-14 09:34:01 +0100953 nla_nest_end(msg, nl_cmds);
954
Johannes Berg7c4ef712011-11-18 15:33:48 +0100955 if (dev->ops->remain_on_channel &&
956 dev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL)
Johannes Berga2939112010-12-14 17:54:28 +0100957 NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
958 dev->wiphy.max_remain_on_channel_duration);
959
Johannes Berg7c4ef712011-11-18 15:33:48 +0100960 if (dev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX)
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100961 NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
962
Johannes Berg2e161f72010-08-12 15:38:38 +0200963 if (mgmt_stypes) {
964 u16 stypes;
965 struct nlattr *nl_ftypes, *nl_ifs;
966 enum nl80211_iftype ift;
967
968 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
969 if (!nl_ifs)
970 goto nla_put_failure;
971
972 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
973 nl_ftypes = nla_nest_start(msg, ift);
974 if (!nl_ftypes)
975 goto nla_put_failure;
976 i = 0;
977 stypes = mgmt_stypes[ift].tx;
978 while (stypes) {
979 if (stypes & 1)
980 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
981 (i << 4) | IEEE80211_FTYPE_MGMT);
982 stypes >>= 1;
983 i++;
984 }
985 nla_nest_end(msg, nl_ftypes);
986 }
987
Johannes Berg74b70a42010-08-24 12:15:53 +0200988 nla_nest_end(msg, nl_ifs);
989
Johannes Berg2e161f72010-08-12 15:38:38 +0200990 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
991 if (!nl_ifs)
992 goto nla_put_failure;
993
994 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
995 nl_ftypes = nla_nest_start(msg, ift);
996 if (!nl_ftypes)
997 goto nla_put_failure;
998 i = 0;
999 stypes = mgmt_stypes[ift].rx;
1000 while (stypes) {
1001 if (stypes & 1)
1002 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
1003 (i << 4) | IEEE80211_FTYPE_MGMT);
1004 stypes >>= 1;
1005 i++;
1006 }
1007 nla_nest_end(msg, nl_ftypes);
1008 }
1009 nla_nest_end(msg, nl_ifs);
1010 }
1011
Johannes Bergff1b6e62011-05-04 15:37:28 +02001012 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
1013 struct nlattr *nl_wowlan;
1014
1015 nl_wowlan = nla_nest_start(msg,
1016 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
1017 if (!nl_wowlan)
1018 goto nla_put_failure;
1019
1020 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
1021 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
1022 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
1023 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
1024 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
1025 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02001026 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
1027 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
1028 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
1029 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
1030 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
1031 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
1032 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
1033 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
1034 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
1035 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02001036 if (dev->wiphy.wowlan.n_patterns) {
1037 struct nl80211_wowlan_pattern_support pat = {
1038 .max_patterns = dev->wiphy.wowlan.n_patterns,
1039 .min_pattern_len =
1040 dev->wiphy.wowlan.pattern_min_len,
1041 .max_pattern_len =
1042 dev->wiphy.wowlan.pattern_max_len,
1043 };
1044 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1045 sizeof(pat), &pat);
1046 }
1047
1048 nla_nest_end(msg, nl_wowlan);
1049 }
1050
Johannes Berg7527a782011-05-13 10:58:57 +02001051 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1052 dev->wiphy.software_iftypes))
1053 goto nla_put_failure;
1054
1055 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1056 goto nla_put_failure;
1057
Johannes Berg562a7482011-11-07 12:39:33 +01001058 if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME)
1059 NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME,
1060 dev->wiphy.ap_sme_capa);
1061
Johannes Berg1f074bd2011-11-06 14:13:33 +01001062 NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features);
1063
Ben Greear7e7c8922011-11-18 11:31:59 -08001064 if (dev->wiphy.ht_capa_mod_mask)
1065 NLA_PUT(msg, NL80211_ATTR_HT_CAPABILITY_MASK,
1066 sizeof(*dev->wiphy.ht_capa_mod_mask),
1067 dev->wiphy.ht_capa_mod_mask);
1068
Johannes Berg55682962007-09-20 13:09:35 -04001069 return genlmsg_end(msg, hdr);
1070
1071 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001072 genlmsg_cancel(msg, hdr);
1073 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001074}
1075
1076static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1077{
1078 int idx = 0;
1079 int start = cb->args[0];
1080 struct cfg80211_registered_device *dev;
1081
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001082 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001083 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001084 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1085 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001086 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001087 continue;
1088 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
1089 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001090 dev) < 0) {
1091 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001092 break;
Julius Volzb4637272008-07-08 14:02:19 +02001093 }
Johannes Berg55682962007-09-20 13:09:35 -04001094 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001095 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001096
1097 cb->args[0] = idx;
1098
1099 return skb->len;
1100}
1101
1102static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1103{
1104 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001105 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001106
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001107 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001108 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001109 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001110
Johannes Berg4c476992010-10-04 21:36:35 +02001111 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1112 nlmsg_free(msg);
1113 return -ENOBUFS;
1114 }
Johannes Berg55682962007-09-20 13:09:35 -04001115
Johannes Berg134e6372009-07-10 09:51:34 +00001116 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001117}
1118
Jouni Malinen31888482008-10-30 16:59:24 +02001119static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1120 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1121 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1122 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1123 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1124 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1125};
1126
1127static int parse_txq_params(struct nlattr *tb[],
1128 struct ieee80211_txq_params *txq_params)
1129{
1130 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
1131 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1132 !tb[NL80211_TXQ_ATTR_AIFS])
1133 return -EINVAL;
1134
1135 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
1136 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1137 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1138 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1139 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1140
1141 return 0;
1142}
1143
Johannes Bergf444de02010-05-05 15:25:02 +02001144static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1145{
1146 /*
1147 * You can only set the channel explicitly for AP, mesh
1148 * and WDS type interfaces; all others have their channel
1149 * managed via their respective "establish a connection"
1150 * command (connect, join, ...)
1151 *
1152 * Monitors are special as they are normally slaved to
1153 * whatever else is going on, so they behave as though
1154 * you tried setting the wiphy channel itself.
1155 */
1156 return !wdev ||
1157 wdev->iftype == NL80211_IFTYPE_AP ||
1158 wdev->iftype == NL80211_IFTYPE_WDS ||
1159 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001160 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1161 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001162}
1163
1164static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1165 struct wireless_dev *wdev,
1166 struct genl_info *info)
1167{
1168 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1169 u32 freq;
1170 int result;
1171
1172 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1173 return -EINVAL;
1174
1175 if (!nl80211_can_set_dev_channel(wdev))
1176 return -EOPNOTSUPP;
1177
1178 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1179 channel_type = nla_get_u32(info->attrs[
1180 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1181 if (channel_type != NL80211_CHAN_NO_HT &&
1182 channel_type != NL80211_CHAN_HT20 &&
1183 channel_type != NL80211_CHAN_HT40PLUS &&
1184 channel_type != NL80211_CHAN_HT40MINUS)
1185 return -EINVAL;
1186 }
1187
1188 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1189
1190 mutex_lock(&rdev->devlist_mtx);
1191 if (wdev) {
1192 wdev_lock(wdev);
1193 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
1194 wdev_unlock(wdev);
1195 } else {
1196 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
1197 }
1198 mutex_unlock(&rdev->devlist_mtx);
1199
1200 return result;
1201}
1202
1203static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1204{
Johannes Berg4c476992010-10-04 21:36:35 +02001205 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1206 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001207
Johannes Berg4c476992010-10-04 21:36:35 +02001208 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001209}
1210
Bill Jordane8347eb2010-10-01 13:54:28 -04001211static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1212{
Johannes Berg43b19952010-10-07 13:10:30 +02001213 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1214 struct net_device *dev = info->user_ptr[1];
1215 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001216 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001217
1218 if (!info->attrs[NL80211_ATTR_MAC])
1219 return -EINVAL;
1220
Johannes Berg43b19952010-10-07 13:10:30 +02001221 if (netif_running(dev))
1222 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001223
Johannes Berg43b19952010-10-07 13:10:30 +02001224 if (!rdev->ops->set_wds_peer)
1225 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001226
Johannes Berg43b19952010-10-07 13:10:30 +02001227 if (wdev->iftype != NL80211_IFTYPE_WDS)
1228 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001229
1230 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001231 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001232}
1233
1234
Johannes Berg55682962007-09-20 13:09:35 -04001235static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1236{
1237 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001238 struct net_device *netdev = NULL;
1239 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001240 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001241 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001242 u32 changed;
1243 u8 retry_short = 0, retry_long = 0;
1244 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001245 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001246
Johannes Bergf444de02010-05-05 15:25:02 +02001247 /*
1248 * Try to find the wiphy and netdev. Normally this
1249 * function shouldn't need the netdev, but this is
1250 * done for backward compatibility -- previously
1251 * setting the channel was done per wiphy, but now
1252 * it is per netdev. Previous userland like hostapd
1253 * also passed a netdev to set_wiphy, so that it is
1254 * possible to let that go to the right netdev!
1255 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001256 mutex_lock(&cfg80211_mutex);
1257
Johannes Bergf444de02010-05-05 15:25:02 +02001258 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1259 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1260
1261 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1262 if (netdev && netdev->ieee80211_ptr) {
1263 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1264 mutex_lock(&rdev->mtx);
1265 } else
1266 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001267 }
1268
Johannes Bergf444de02010-05-05 15:25:02 +02001269 if (!netdev) {
1270 rdev = __cfg80211_rdev_from_info(info);
1271 if (IS_ERR(rdev)) {
1272 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001273 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001274 }
1275 wdev = NULL;
1276 netdev = NULL;
1277 result = 0;
1278
1279 mutex_lock(&rdev->mtx);
1280 } else if (netif_running(netdev) &&
1281 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
1282 wdev = netdev->ieee80211_ptr;
1283 else
1284 wdev = NULL;
1285
1286 /*
1287 * end workaround code, by now the rdev is available
1288 * and locked, and wdev may or may not be NULL.
1289 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001290
1291 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001292 result = cfg80211_dev_rename(
1293 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001294
1295 mutex_unlock(&cfg80211_mutex);
1296
1297 if (result)
1298 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001299
Jouni Malinen31888482008-10-30 16:59:24 +02001300 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1301 struct ieee80211_txq_params txq_params;
1302 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1303
1304 if (!rdev->ops->set_txq_params) {
1305 result = -EOPNOTSUPP;
1306 goto bad_res;
1307 }
1308
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001309 if (!netdev) {
1310 result = -EINVAL;
1311 goto bad_res;
1312 }
1313
Johannes Berg133a3ff2011-11-03 14:50:13 +01001314 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1315 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1316 result = -EINVAL;
1317 goto bad_res;
1318 }
1319
Johannes Berg2b5f8b02012-04-02 10:51:55 +02001320 if (!netif_running(netdev)) {
1321 result = -ENETDOWN;
1322 goto bad_res;
1323 }
1324
Jouni Malinen31888482008-10-30 16:59:24 +02001325 nla_for_each_nested(nl_txq_params,
1326 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1327 rem_txq_params) {
1328 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1329 nla_data(nl_txq_params),
1330 nla_len(nl_txq_params),
1331 txq_params_policy);
1332 result = parse_txq_params(tb, &txq_params);
1333 if (result)
1334 goto bad_res;
1335
1336 result = rdev->ops->set_txq_params(&rdev->wiphy,
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001337 netdev,
Jouni Malinen31888482008-10-30 16:59:24 +02001338 &txq_params);
1339 if (result)
1340 goto bad_res;
1341 }
1342 }
1343
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001344 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001345 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001346 if (result)
1347 goto bad_res;
1348 }
1349
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001350 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1351 enum nl80211_tx_power_setting type;
1352 int idx, mbm = 0;
1353
1354 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001355 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001356 goto bad_res;
1357 }
1358
1359 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1360 type = nla_get_u32(info->attrs[idx]);
1361
1362 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1363 (type != NL80211_TX_POWER_AUTOMATIC)) {
1364 result = -EINVAL;
1365 goto bad_res;
1366 }
1367
1368 if (type != NL80211_TX_POWER_AUTOMATIC) {
1369 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1370 mbm = nla_get_u32(info->attrs[idx]);
1371 }
1372
1373 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1374 if (result)
1375 goto bad_res;
1376 }
1377
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001378 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1379 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1380 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001381 if ((!rdev->wiphy.available_antennas_tx &&
1382 !rdev->wiphy.available_antennas_rx) ||
1383 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001384 result = -EOPNOTSUPP;
1385 goto bad_res;
1386 }
1387
1388 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1389 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1390
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001391 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001392 * available antenna masks, except for the "all" mask */
1393 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1394 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001395 result = -EINVAL;
1396 goto bad_res;
1397 }
1398
Bruno Randolf7f531e02010-12-16 11:30:22 +09001399 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1400 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001401
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001402 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1403 if (result)
1404 goto bad_res;
1405 }
1406
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001407 changed = 0;
1408
1409 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1410 retry_short = nla_get_u8(
1411 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1412 if (retry_short == 0) {
1413 result = -EINVAL;
1414 goto bad_res;
1415 }
1416 changed |= WIPHY_PARAM_RETRY_SHORT;
1417 }
1418
1419 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1420 retry_long = nla_get_u8(
1421 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1422 if (retry_long == 0) {
1423 result = -EINVAL;
1424 goto bad_res;
1425 }
1426 changed |= WIPHY_PARAM_RETRY_LONG;
1427 }
1428
1429 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1430 frag_threshold = nla_get_u32(
1431 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1432 if (frag_threshold < 256) {
1433 result = -EINVAL;
1434 goto bad_res;
1435 }
1436 if (frag_threshold != (u32) -1) {
1437 /*
1438 * Fragments (apart from the last one) are required to
1439 * have even length. Make the fragmentation code
1440 * simpler by stripping LSB should someone try to use
1441 * odd threshold value.
1442 */
1443 frag_threshold &= ~0x1;
1444 }
1445 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1446 }
1447
1448 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1449 rts_threshold = nla_get_u32(
1450 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1451 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1452 }
1453
Lukáš Turek81077e82009-12-21 22:50:47 +01001454 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1455 coverage_class = nla_get_u8(
1456 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1457 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1458 }
1459
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001460 if (changed) {
1461 u8 old_retry_short, old_retry_long;
1462 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001463 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001464
1465 if (!rdev->ops->set_wiphy_params) {
1466 result = -EOPNOTSUPP;
1467 goto bad_res;
1468 }
1469
1470 old_retry_short = rdev->wiphy.retry_short;
1471 old_retry_long = rdev->wiphy.retry_long;
1472 old_frag_threshold = rdev->wiphy.frag_threshold;
1473 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001474 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001475
1476 if (changed & WIPHY_PARAM_RETRY_SHORT)
1477 rdev->wiphy.retry_short = retry_short;
1478 if (changed & WIPHY_PARAM_RETRY_LONG)
1479 rdev->wiphy.retry_long = retry_long;
1480 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1481 rdev->wiphy.frag_threshold = frag_threshold;
1482 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1483 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001484 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1485 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001486
1487 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1488 if (result) {
1489 rdev->wiphy.retry_short = old_retry_short;
1490 rdev->wiphy.retry_long = old_retry_long;
1491 rdev->wiphy.frag_threshold = old_frag_threshold;
1492 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001493 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001494 }
1495 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001496
Johannes Berg306d6112008-12-08 12:39:04 +01001497 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001498 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001499 if (netdev)
1500 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001501 return result;
1502}
1503
1504
1505static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001506 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001507 struct net_device *dev)
1508{
1509 void *hdr;
1510
1511 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1512 if (!hdr)
1513 return -1;
1514
1515 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001516 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001517 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001518 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001519
1520 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1521 rdev->devlist_generation ^
1522 (cfg80211_rdev_list_generation << 2));
1523
Johannes Berg55682962007-09-20 13:09:35 -04001524 return genlmsg_end(msg, hdr);
1525
1526 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001527 genlmsg_cancel(msg, hdr);
1528 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001529}
1530
1531static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1532{
1533 int wp_idx = 0;
1534 int if_idx = 0;
1535 int wp_start = cb->args[0];
1536 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001537 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001538 struct wireless_dev *wdev;
1539
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001540 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001541 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1542 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001543 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001544 if (wp_idx < wp_start) {
1545 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001546 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001547 }
Johannes Berg55682962007-09-20 13:09:35 -04001548 if_idx = 0;
1549
Johannes Bergf5ea9122009-08-07 16:17:38 +02001550 mutex_lock(&rdev->devlist_mtx);
1551 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001552 if (if_idx < if_start) {
1553 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001554 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001555 }
Johannes Berg55682962007-09-20 13:09:35 -04001556 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1557 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001558 rdev, wdev->netdev) < 0) {
1559 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001560 goto out;
1561 }
1562 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001563 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001564 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001565
1566 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001567 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001568 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001569 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001570
1571 cb->args[0] = wp_idx;
1572 cb->args[1] = if_idx;
1573
1574 return skb->len;
1575}
1576
1577static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1578{
1579 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001580 struct cfg80211_registered_device *dev = info->user_ptr[0];
1581 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001582
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001583 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001584 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001585 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001586
Johannes Bergd7264052009-04-19 16:23:20 +02001587 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001588 dev, netdev) < 0) {
1589 nlmsg_free(msg);
1590 return -ENOBUFS;
1591 }
Johannes Berg55682962007-09-20 13:09:35 -04001592
Johannes Berg134e6372009-07-10 09:51:34 +00001593 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001594}
1595
Michael Wu66f7ac52008-01-31 19:48:22 +01001596static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1597 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1598 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1599 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1600 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1601 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1602};
1603
1604static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1605{
1606 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1607 int flag;
1608
1609 *mntrflags = 0;
1610
1611 if (!nla)
1612 return -EINVAL;
1613
1614 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1615 nla, mntr_flags_policy))
1616 return -EINVAL;
1617
1618 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1619 if (flags[flag])
1620 *mntrflags |= (1<<flag);
1621
1622 return 0;
1623}
1624
Johannes Berg9bc383d2009-11-19 11:55:19 +01001625static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001626 struct net_device *netdev, u8 use_4addr,
1627 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001628{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001629 if (!use_4addr) {
Jiri Pirkof350a0a2010-06-15 06:50:45 +00001630 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001631 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001632 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001633 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001634
1635 switch (iftype) {
1636 case NL80211_IFTYPE_AP_VLAN:
1637 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1638 return 0;
1639 break;
1640 case NL80211_IFTYPE_STATION:
1641 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1642 return 0;
1643 break;
1644 default:
1645 break;
1646 }
1647
1648 return -EOPNOTSUPP;
1649}
1650
Johannes Berg55682962007-09-20 13:09:35 -04001651static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1652{
Johannes Berg4c476992010-10-04 21:36:35 +02001653 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001654 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001655 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001656 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001657 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001658 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001659 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001660
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001661 memset(&params, 0, sizeof(params));
1662
Johannes Berg04a773a2009-04-19 21:24:32 +02001663 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001664
Johannes Berg723b0382008-09-16 20:22:09 +02001665 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001666 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001667 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001668 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001669 if (ntype > NL80211_IFTYPE_MAX)
1670 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001671 }
1672
Johannes Berg92ffe052008-09-16 20:39:36 +02001673 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001674 struct wireless_dev *wdev = dev->ieee80211_ptr;
1675
Johannes Berg4c476992010-10-04 21:36:35 +02001676 if (ntype != NL80211_IFTYPE_MESH_POINT)
1677 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001678 if (netif_running(dev))
1679 return -EBUSY;
1680
1681 wdev_lock(wdev);
1682 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1683 IEEE80211_MAX_MESH_ID_LEN);
1684 wdev->mesh_id_up_len =
1685 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1686 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1687 wdev->mesh_id_up_len);
1688 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001689 }
1690
Felix Fietkau8b787642009-11-10 18:53:10 +01001691 if (info->attrs[NL80211_ATTR_4ADDR]) {
1692 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1693 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001694 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001695 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001696 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001697 } else {
1698 params.use_4addr = -1;
1699 }
1700
Johannes Berg92ffe052008-09-16 20:39:36 +02001701 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001702 if (ntype != NL80211_IFTYPE_MONITOR)
1703 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001704 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1705 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001706 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001707 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001708
1709 flags = &_flags;
1710 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001711 }
Johannes Berg3b858752009-03-12 09:55:09 +01001712
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001713 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001714 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001715 else
1716 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001717
Johannes Berg9bc383d2009-11-19 11:55:19 +01001718 if (!err && params.use_4addr != -1)
1719 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1720
Johannes Berg55682962007-09-20 13:09:35 -04001721 return err;
1722}
1723
1724static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1725{
Johannes Berg4c476992010-10-04 21:36:35 +02001726 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001727 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001728 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001729 int err;
1730 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001731 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001732
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001733 memset(&params, 0, sizeof(params));
1734
Johannes Berg55682962007-09-20 13:09:35 -04001735 if (!info->attrs[NL80211_ATTR_IFNAME])
1736 return -EINVAL;
1737
1738 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1739 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1740 if (type > NL80211_IFTYPE_MAX)
1741 return -EINVAL;
1742 }
1743
Johannes Berg79c97e92009-07-07 03:56:12 +02001744 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001745 !(rdev->wiphy.interface_modes & (1 << type)))
1746 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001747
Johannes Berg9bc383d2009-11-19 11:55:19 +01001748 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001749 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001750 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001751 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001752 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001753 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001754
Michael Wu66f7ac52008-01-31 19:48:22 +01001755 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1756 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1757 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001758 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001759 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001760 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001761 if (IS_ERR(dev))
1762 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001763
Johannes Berg29cbe682010-12-03 09:20:44 +01001764 if (type == NL80211_IFTYPE_MESH_POINT &&
1765 info->attrs[NL80211_ATTR_MESH_ID]) {
1766 struct wireless_dev *wdev = dev->ieee80211_ptr;
1767
1768 wdev_lock(wdev);
1769 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1770 IEEE80211_MAX_MESH_ID_LEN);
1771 wdev->mesh_id_up_len =
1772 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1773 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1774 wdev->mesh_id_up_len);
1775 wdev_unlock(wdev);
1776 }
1777
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001778 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001779}
1780
1781static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1782{
Johannes Berg4c476992010-10-04 21:36:35 +02001783 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1784 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001785
Johannes Berg4c476992010-10-04 21:36:35 +02001786 if (!rdev->ops->del_virtual_intf)
1787 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001788
Johannes Berg4c476992010-10-04 21:36:35 +02001789 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001790}
1791
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01001792static int nl80211_set_noack_map(struct sk_buff *skb, struct genl_info *info)
1793{
1794 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1795 struct net_device *dev = info->user_ptr[1];
1796 u16 noack_map;
1797
1798 if (!info->attrs[NL80211_ATTR_NOACK_MAP])
1799 return -EINVAL;
1800
1801 if (!rdev->ops->set_noack_map)
1802 return -EOPNOTSUPP;
1803
1804 noack_map = nla_get_u16(info->attrs[NL80211_ATTR_NOACK_MAP]);
1805
1806 return rdev->ops->set_noack_map(&rdev->wiphy, dev, noack_map);
1807}
1808
Johannes Berg41ade002007-12-19 02:03:29 +01001809struct get_key_cookie {
1810 struct sk_buff *msg;
1811 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001812 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001813};
1814
1815static void get_key_callback(void *c, struct key_params *params)
1816{
Johannes Bergb9454e82009-07-08 13:29:08 +02001817 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001818 struct get_key_cookie *cookie = c;
1819
1820 if (params->key)
1821 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1822 params->key_len, params->key);
1823
1824 if (params->seq)
1825 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1826 params->seq_len, params->seq);
1827
1828 if (params->cipher)
1829 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1830 params->cipher);
1831
Johannes Bergb9454e82009-07-08 13:29:08 +02001832 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1833 if (!key)
1834 goto nla_put_failure;
1835
1836 if (params->key)
1837 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1838 params->key_len, params->key);
1839
1840 if (params->seq)
1841 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1842 params->seq_len, params->seq);
1843
1844 if (params->cipher)
1845 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1846 params->cipher);
1847
1848 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1849
1850 nla_nest_end(cookie->msg, key);
1851
Johannes Berg41ade002007-12-19 02:03:29 +01001852 return;
1853 nla_put_failure:
1854 cookie->error = 1;
1855}
1856
1857static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1858{
Johannes Berg4c476992010-10-04 21:36:35 +02001859 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001860 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001861 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001862 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001863 const u8 *mac_addr = NULL;
1864 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001865 struct get_key_cookie cookie = {
1866 .error = 0,
1867 };
1868 void *hdr;
1869 struct sk_buff *msg;
1870
1871 if (info->attrs[NL80211_ATTR_KEY_IDX])
1872 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1873
Jouni Malinen3cfcf6a2009-01-08 13:32:02 +02001874 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001875 return -EINVAL;
1876
1877 if (info->attrs[NL80211_ATTR_MAC])
1878 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1879
Johannes Berge31b8212010-10-05 19:39:30 +02001880 pairwise = !!mac_addr;
1881 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1882 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1883 if (kt >= NUM_NL80211_KEYTYPES)
1884 return -EINVAL;
1885 if (kt != NL80211_KEYTYPE_GROUP &&
1886 kt != NL80211_KEYTYPE_PAIRWISE)
1887 return -EINVAL;
1888 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1889 }
1890
Johannes Berg4c476992010-10-04 21:36:35 +02001891 if (!rdev->ops->get_key)
1892 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001893
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001894 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001895 if (!msg)
1896 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001897
1898 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1899 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001900 if (IS_ERR(hdr))
1901 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001902
1903 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001904 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001905
1906 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1907 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1908 if (mac_addr)
1909 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1910
Johannes Berge31b8212010-10-05 19:39:30 +02001911 if (pairwise && mac_addr &&
1912 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1913 return -ENOENT;
1914
1915 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1916 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001917
1918 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001919 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001920
1921 if (cookie.error)
1922 goto nla_put_failure;
1923
1924 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001925 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001926
1927 nla_put_failure:
1928 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001929 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001930 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001931 return err;
1932}
1933
1934static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1935{
Johannes Berg4c476992010-10-04 21:36:35 +02001936 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001937 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001938 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001939 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001940
Johannes Bergb9454e82009-07-08 13:29:08 +02001941 err = nl80211_parse_key(info, &key);
1942 if (err)
1943 return err;
1944
1945 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001946 return -EINVAL;
1947
Johannes Bergb9454e82009-07-08 13:29:08 +02001948 /* only support setting default key */
1949 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001950 return -EINVAL;
1951
Johannes Bergfffd0932009-07-08 14:22:54 +02001952 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001953
1954 if (key.def) {
1955 if (!rdev->ops->set_default_key) {
1956 err = -EOPNOTSUPP;
1957 goto out;
1958 }
1959
1960 err = nl80211_key_allowed(dev->ieee80211_ptr);
1961 if (err)
1962 goto out;
1963
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001964 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
1965 key.def_uni, key.def_multi);
1966
1967 if (err)
1968 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02001969
Johannes Berg3d23e342009-09-29 23:27:28 +02001970#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001971 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001972#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001973 } else {
1974 if (key.def_uni || !key.def_multi) {
1975 err = -EINVAL;
1976 goto out;
1977 }
1978
1979 if (!rdev->ops->set_default_mgmt_key) {
1980 err = -EOPNOTSUPP;
1981 goto out;
1982 }
1983
1984 err = nl80211_key_allowed(dev->ieee80211_ptr);
1985 if (err)
1986 goto out;
1987
1988 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
1989 dev, key.idx);
1990 if (err)
1991 goto out;
1992
1993#ifdef CONFIG_CFG80211_WEXT
1994 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
1995#endif
1996 }
1997
1998 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02001999 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002000
Johannes Berg41ade002007-12-19 02:03:29 +01002001 return err;
2002}
2003
2004static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
2005{
Johannes Berg4c476992010-10-04 21:36:35 +02002006 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02002007 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002008 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02002009 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02002010 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01002011
Johannes Bergb9454e82009-07-08 13:29:08 +02002012 err = nl80211_parse_key(info, &key);
2013 if (err)
2014 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002015
Johannes Bergb9454e82009-07-08 13:29:08 +02002016 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01002017 return -EINVAL;
2018
Johannes Berg41ade002007-12-19 02:03:29 +01002019 if (info->attrs[NL80211_ATTR_MAC])
2020 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2021
Johannes Berge31b8212010-10-05 19:39:30 +02002022 if (key.type == -1) {
2023 if (mac_addr)
2024 key.type = NL80211_KEYTYPE_PAIRWISE;
2025 else
2026 key.type = NL80211_KEYTYPE_GROUP;
2027 }
2028
2029 /* for now */
2030 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2031 key.type != NL80211_KEYTYPE_GROUP)
2032 return -EINVAL;
2033
Johannes Berg4c476992010-10-04 21:36:35 +02002034 if (!rdev->ops->add_key)
2035 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002036
Johannes Berge31b8212010-10-05 19:39:30 +02002037 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
2038 key.type == NL80211_KEYTYPE_PAIRWISE,
2039 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02002040 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02002041
2042 wdev_lock(dev->ieee80211_ptr);
2043 err = nl80211_key_allowed(dev->ieee80211_ptr);
2044 if (!err)
2045 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02002046 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02002047 mac_addr, &key.p);
2048 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01002049
Johannes Berg41ade002007-12-19 02:03:29 +01002050 return err;
2051}
2052
2053static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
2054{
Johannes Berg4c476992010-10-04 21:36:35 +02002055 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01002056 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002057 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002058 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002059 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002060
Johannes Bergb9454e82009-07-08 13:29:08 +02002061 err = nl80211_parse_key(info, &key);
2062 if (err)
2063 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002064
2065 if (info->attrs[NL80211_ATTR_MAC])
2066 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2067
Johannes Berge31b8212010-10-05 19:39:30 +02002068 if (key.type == -1) {
2069 if (mac_addr)
2070 key.type = NL80211_KEYTYPE_PAIRWISE;
2071 else
2072 key.type = NL80211_KEYTYPE_GROUP;
2073 }
2074
2075 /* for now */
2076 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2077 key.type != NL80211_KEYTYPE_GROUP)
2078 return -EINVAL;
2079
Johannes Berg4c476992010-10-04 21:36:35 +02002080 if (!rdev->ops->del_key)
2081 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002082
Johannes Bergfffd0932009-07-08 14:22:54 +02002083 wdev_lock(dev->ieee80211_ptr);
2084 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002085
2086 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2087 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2088 err = -ENOENT;
2089
Johannes Bergfffd0932009-07-08 14:22:54 +02002090 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02002091 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
2092 key.type == NL80211_KEYTYPE_PAIRWISE,
2093 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002094
Johannes Berg3d23e342009-09-29 23:27:28 +02002095#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002096 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002097 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002098 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002099 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002100 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2101 }
2102#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002103 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002104
Johannes Berg41ade002007-12-19 02:03:29 +01002105 return err;
2106}
2107
Johannes Berg88600202012-02-13 15:17:18 +01002108static int nl80211_parse_beacon(struct genl_info *info,
2109 struct cfg80211_beacon_data *bcn)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002110{
Johannes Berg88600202012-02-13 15:17:18 +01002111 bool haveinfo = false;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002112
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002113 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2114 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2115 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2116 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002117 return -EINVAL;
2118
Johannes Berg88600202012-02-13 15:17:18 +01002119 memset(bcn, 0, sizeof(*bcn));
Johannes Berged1b6cc2007-12-19 02:03:32 +01002120
Johannes Berged1b6cc2007-12-19 02:03:32 +01002121 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
Johannes Berg88600202012-02-13 15:17:18 +01002122 bcn->head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2123 bcn->head_len = nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2124 if (!bcn->head_len)
2125 return -EINVAL;
2126 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002127 }
2128
2129 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
Johannes Berg88600202012-02-13 15:17:18 +01002130 bcn->tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2131 bcn->tail_len =
Johannes Berged1b6cc2007-12-19 02:03:32 +01002132 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
Johannes Berg88600202012-02-13 15:17:18 +01002133 haveinfo = true;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002134 }
2135
Johannes Berg4c476992010-10-04 21:36:35 +02002136 if (!haveinfo)
2137 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002138
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002139 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg88600202012-02-13 15:17:18 +01002140 bcn->beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2141 bcn->beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002142 }
2143
2144 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002145 bcn->proberesp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002146 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002147 bcn->proberesp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002148 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2149 }
2150
2151 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002152 bcn->assocresp_ies =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002153 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002154 bcn->assocresp_ies_len =
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002155 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2156 }
2157
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002158 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
Johannes Berg88600202012-02-13 15:17:18 +01002159 bcn->probe_resp =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002160 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
Johannes Berg88600202012-02-13 15:17:18 +01002161 bcn->probe_resp_len =
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002162 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2163 }
2164
Johannes Berg88600202012-02-13 15:17:18 +01002165 return 0;
2166}
2167
2168static int nl80211_start_ap(struct sk_buff *skb, struct genl_info *info)
2169{
2170 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2171 struct net_device *dev = info->user_ptr[1];
2172 struct wireless_dev *wdev = dev->ieee80211_ptr;
2173 struct cfg80211_ap_settings params;
2174 int err;
2175
2176 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2177 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2178 return -EOPNOTSUPP;
2179
2180 if (!rdev->ops->start_ap)
2181 return -EOPNOTSUPP;
2182
2183 if (wdev->beacon_interval)
2184 return -EALREADY;
2185
2186 memset(&params, 0, sizeof(params));
2187
2188 /* these are required for START_AP */
2189 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2190 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
2191 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2192 return -EINVAL;
2193
2194 err = nl80211_parse_beacon(info, &params.beacon);
2195 if (err)
2196 return err;
2197
2198 params.beacon_interval =
2199 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2200 params.dtim_period =
2201 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2202
2203 err = cfg80211_validate_beacon_int(rdev, params.beacon_interval);
2204 if (err)
2205 return err;
2206
2207 /*
2208 * In theory, some of these attributes should be required here
2209 * but since they were not used when the command was originally
2210 * added, keep them optional for old user space programs to let
2211 * them continue to work with drivers that do not need the
2212 * additional information -- drivers must check!
2213 */
2214 if (info->attrs[NL80211_ATTR_SSID]) {
2215 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2216 params.ssid_len =
2217 nla_len(info->attrs[NL80211_ATTR_SSID]);
2218 if (params.ssid_len == 0 ||
2219 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2220 return -EINVAL;
2221 }
2222
2223 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2224 params.hidden_ssid = nla_get_u32(
2225 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2226 if (params.hidden_ssid != NL80211_HIDDEN_SSID_NOT_IN_USE &&
2227 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_LEN &&
2228 params.hidden_ssid != NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2229 return -EINVAL;
2230 }
2231
2232 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2233
2234 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2235 params.auth_type = nla_get_u32(
2236 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2237 if (!nl80211_valid_auth_type(params.auth_type))
2238 return -EINVAL;
2239 } else
2240 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2241
2242 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2243 NL80211_MAX_NR_CIPHER_SUITES);
2244 if (err)
2245 return err;
2246
Vasanthakumar Thiagarajan1b658f12012-03-02 15:50:02 +05302247 if (info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]) {
2248 if (!(rdev->wiphy.features & NL80211_FEATURE_INACTIVITY_TIMER))
2249 return -EOPNOTSUPP;
2250 params.inactivity_timeout = nla_get_u16(
2251 info->attrs[NL80211_ATTR_INACTIVITY_TIMEOUT]);
2252 }
2253
Johannes Berg88600202012-02-13 15:17:18 +01002254 err = rdev->ops->start_ap(&rdev->wiphy, dev, &params);
2255 if (!err)
2256 wdev->beacon_interval = params.beacon_interval;
Johannes Berg56d18932011-05-09 18:41:15 +02002257 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002258}
2259
Johannes Berg88600202012-02-13 15:17:18 +01002260static int nl80211_set_beacon(struct sk_buff *skb, struct genl_info *info)
2261{
2262 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2263 struct net_device *dev = info->user_ptr[1];
2264 struct wireless_dev *wdev = dev->ieee80211_ptr;
2265 struct cfg80211_beacon_data params;
2266 int err;
2267
2268 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
2269 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2270 return -EOPNOTSUPP;
2271
2272 if (!rdev->ops->change_beacon)
2273 return -EOPNOTSUPP;
2274
2275 if (!wdev->beacon_interval)
2276 return -EINVAL;
2277
2278 err = nl80211_parse_beacon(info, &params);
2279 if (err)
2280 return err;
2281
2282 return rdev->ops->change_beacon(&rdev->wiphy, dev, &params);
2283}
2284
2285static int nl80211_stop_ap(struct sk_buff *skb, struct genl_info *info)
Johannes Berged1b6cc2007-12-19 02:03:32 +01002286{
Johannes Berg4c476992010-10-04 21:36:35 +02002287 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2288 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002289 struct wireless_dev *wdev = dev->ieee80211_ptr;
2290 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002291
Johannes Berg88600202012-02-13 15:17:18 +01002292 if (!rdev->ops->stop_ap)
Johannes Berg4c476992010-10-04 21:36:35 +02002293 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002294
Johannes Berg074ac8d2010-09-16 14:58:22 +02002295 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002296 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2297 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002298
Johannes Berg88600202012-02-13 15:17:18 +01002299 if (!wdev->beacon_interval)
2300 return -ENOENT;
2301
2302 err = rdev->ops->stop_ap(&rdev->wiphy, dev);
Johannes Berg56d18932011-05-09 18:41:15 +02002303 if (!err)
2304 wdev->beacon_interval = 0;
2305 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002306}
2307
Johannes Berg5727ef12007-12-19 02:03:34 +01002308static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2309 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2310 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2311 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002312 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002313 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Bergd83023d2011-12-14 09:29:15 +01002314 [NL80211_STA_FLAG_TDLS_PEER] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002315};
2316
Johannes Bergeccb8e82009-05-11 21:57:56 +03002317static int parse_station_flags(struct genl_info *info,
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002318 enum nl80211_iftype iftype,
Johannes Bergeccb8e82009-05-11 21:57:56 +03002319 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002320{
2321 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002322 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002323 int flag;
2324
Johannes Bergeccb8e82009-05-11 21:57:56 +03002325 /*
2326 * Try parsing the new attribute first so userspace
2327 * can specify both for older kernels.
2328 */
2329 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2330 if (nla) {
2331 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002332
Johannes Bergeccb8e82009-05-11 21:57:56 +03002333 sta_flags = nla_data(nla);
2334 params->sta_flags_mask = sta_flags->mask;
2335 params->sta_flags_set = sta_flags->set;
2336 if ((params->sta_flags_mask |
2337 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2338 return -EINVAL;
2339 return 0;
2340 }
2341
2342 /* if present, parse the old attribute */
2343
2344 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002345 if (!nla)
2346 return 0;
2347
2348 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2349 nla, sta_flags_policy))
2350 return -EINVAL;
2351
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002352 /*
2353 * Only allow certain flags for interface types so that
2354 * other attributes are silently ignored. Remember that
2355 * this is backward compatibility code with old userspace
2356 * and shouldn't be hit in other cases anyway.
2357 */
2358 switch (iftype) {
2359 case NL80211_IFTYPE_AP:
2360 case NL80211_IFTYPE_AP_VLAN:
2361 case NL80211_IFTYPE_P2P_GO:
2362 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2363 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2364 BIT(NL80211_STA_FLAG_WME) |
2365 BIT(NL80211_STA_FLAG_MFP);
2366 break;
2367 case NL80211_IFTYPE_P2P_CLIENT:
2368 case NL80211_IFTYPE_STATION:
2369 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHORIZED) |
2370 BIT(NL80211_STA_FLAG_TDLS_PEER);
2371 break;
2372 case NL80211_IFTYPE_MESH_POINT:
2373 params->sta_flags_mask = BIT(NL80211_STA_FLAG_AUTHENTICATED) |
2374 BIT(NL80211_STA_FLAG_MFP) |
2375 BIT(NL80211_STA_FLAG_AUTHORIZED);
2376 default:
2377 return -EINVAL;
2378 }
Johannes Berg5727ef12007-12-19 02:03:34 +01002379
2380 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
2381 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03002382 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002383
2384 return 0;
2385}
2386
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002387static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2388 int attr)
2389{
2390 struct nlattr *rate;
2391 u16 bitrate;
2392
2393 rate = nla_nest_start(msg, attr);
2394 if (!rate)
Johannes Berg99958b22013-03-19 14:28:37 -07002395 return false;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002396
2397 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2398 bitrate = cfg80211_calculate_bitrate(info);
2399 if (bitrate > 0)
Johannes Berg99958b22013-03-19 14:28:37 -07002400 nla_put_u16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002401
Johannes Berg99958b22013-03-19 14:28:37 -07002402 if (info->flags & RATE_INFO_FLAGS_MCS) {
2403 if (nla_put_u8(msg, NL80211_RATE_INFO_MCS, info->mcs))
2404 return false;
2405 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
2406 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
2407 return false;
2408 if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
2409 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
2410 return false;
2411 } else if (info->flags & RATE_INFO_FLAGS_VHT_MCS) {
2412 if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_MCS, info->mcs))
2413 return false;
2414 if (nla_put_u8(msg, NL80211_RATE_INFO_VHT_NSS, info->nss))
2415 return false;
2416 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH &&
2417 nla_put_flag(msg, NL80211_RATE_INFO_40_MHZ_WIDTH))
2418 return false;
2419 if (info->flags & RATE_INFO_FLAGS_80_MHZ_WIDTH &&
2420 nla_put_flag(msg, NL80211_RATE_INFO_80_MHZ_WIDTH))
2421 return false;
2422 if (info->flags & RATE_INFO_FLAGS_80P80_MHZ_WIDTH &&
2423 nla_put_flag(msg, NL80211_RATE_INFO_80P80_MHZ_WIDTH))
2424 return false;
2425 if (info->flags & RATE_INFO_FLAGS_160_MHZ_WIDTH &&
2426 nla_put_flag(msg, NL80211_RATE_INFO_160_MHZ_WIDTH))
2427 return false;
2428 if (info->flags & RATE_INFO_FLAGS_SHORT_GI &&
2429 nla_put_flag(msg, NL80211_RATE_INFO_SHORT_GI))
2430 return false;
2431 }
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002432
2433 nla_nest_end(msg, rate);
2434 return true;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002435}
2436
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002437static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
John W. Linville66266b32012-03-15 13:25:41 -04002438 int flags,
2439 struct cfg80211_registered_device *rdev,
2440 struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002441 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002442{
2443 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002444 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002445
2446 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2447 if (!hdr)
2448 return -1;
2449
2450 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2451 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
2452
Johannes Bergf5ea9122009-08-07 16:17:38 +02002453 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
2454
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002455 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2456 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002457 goto nla_put_failure;
Mohammed Shafi Shajakhanebe27c92011-04-08 21:24:24 +05302458 if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
2459 NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2460 sinfo->connected_time);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002461 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
2462 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2463 sinfo->inactive_time);
2464 if (sinfo->filled & STATION_INFO_RX_BYTES)
2465 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
2466 sinfo->rx_bytes);
2467 if (sinfo->filled & STATION_INFO_TX_BYTES)
2468 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
2469 sinfo->tx_bytes);
2470 if (sinfo->filled & STATION_INFO_LLID)
2471 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
2472 sinfo->llid);
2473 if (sinfo->filled & STATION_INFO_PLID)
2474 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
2475 sinfo->plid);
2476 if (sinfo->filled & STATION_INFO_PLINK_STATE)
2477 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
2478 sinfo->plink_state);
John W. Linville66266b32012-03-15 13:25:41 -04002479 switch (rdev->wiphy.signal_type) {
2480 case CFG80211_SIGNAL_TYPE_MBM:
2481 if (sinfo->filled & STATION_INFO_SIGNAL)
2482 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
2483 sinfo->signal);
2484 if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
2485 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2486 sinfo->signal_avg);
2487 break;
2488 default:
2489 break;
2490 }
Henning Rogge420e7fa2008-12-11 22:04:19 +01002491 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002492 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2493 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002494 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002495 }
2496 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2497 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2498 NL80211_STA_INFO_RX_BITRATE))
2499 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002500 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02002501 if (sinfo->filled & STATION_INFO_RX_PACKETS)
2502 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
2503 sinfo->rx_packets);
2504 if (sinfo->filled & STATION_INFO_TX_PACKETS)
2505 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
2506 sinfo->tx_packets);
Bruno Randolfb206b4e2010-10-06 18:34:12 +09002507 if (sinfo->filled & STATION_INFO_TX_RETRIES)
2508 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES,
2509 sinfo->tx_retries);
2510 if (sinfo->filled & STATION_INFO_TX_FAILED)
2511 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
2512 sinfo->tx_failed);
Paul Stewarta85e1d52011-12-09 11:01:49 -08002513 if (sinfo->filled & STATION_INFO_BEACON_LOSS_COUNT)
2514 NLA_PUT_U32(msg, NL80211_STA_INFO_BEACON_LOSS,
2515 sinfo->beacon_loss_count);
Paul Stewartf4263c92011-03-31 09:25:41 -07002516 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2517 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2518 if (!bss_param)
2519 goto nla_put_failure;
2520
2521 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
2522 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
2523 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
2524 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
2525 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
2526 NLA_PUT_FLAG(msg,
2527 NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
2528 NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2529 sinfo->bss_param.dtim_period);
2530 NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2531 sinfo->bss_param.beacon_interval);
2532
2533 nla_nest_end(msg, bss_param);
2534 }
Helmut Schaabb6e7532011-10-13 16:30:39 +02002535 if (sinfo->filled & STATION_INFO_STA_FLAGS)
2536 NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS,
2537 sizeof(struct nl80211_sta_flag_update),
2538 &sinfo->sta_flags);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002539 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002540
Felix Fietkau040bdf72011-08-10 19:00:33 -06002541 if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002542 NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2543 sinfo->assoc_req_ies);
2544
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002545 return genlmsg_end(msg, hdr);
2546
2547 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002548 genlmsg_cancel(msg, hdr);
2549 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002550}
2551
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002552static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002553 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002554{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002555 struct station_info sinfo;
2556 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002557 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002558 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002559 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002560 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002561
Johannes Berg67748892010-10-04 21:14:06 +02002562 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2563 if (err)
2564 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002565
2566 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002567 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002568 goto out_err;
2569 }
2570
Johannes Bergbba95fe2008-07-29 13:22:51 +02002571 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002572 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002573 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2574 mac_addr, &sinfo);
2575 if (err == -ENOENT)
2576 break;
2577 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002578 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002579
2580 if (nl80211_send_station(skb,
2581 NETLINK_CB(cb->skb).pid,
2582 cb->nlh->nlmsg_seq, NLM_F_MULTI,
John W. Linville66266b32012-03-15 13:25:41 -04002583 dev, netdev, mac_addr,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002584 &sinfo) < 0)
2585 goto out;
2586
2587 sta_idx++;
2588 }
2589
2590
2591 out:
2592 cb->args[1] = sta_idx;
2593 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002594 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002595 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002596
2597 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002598}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002599
Johannes Berg5727ef12007-12-19 02:03:34 +01002600static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2601{
Johannes Berg4c476992010-10-04 21:36:35 +02002602 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2603 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002604 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002605 struct sk_buff *msg;
2606 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002607 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002608
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002609 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002610
2611 if (!info->attrs[NL80211_ATTR_MAC])
2612 return -EINVAL;
2613
2614 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2615
Johannes Berg4c476992010-10-04 21:36:35 +02002616 if (!rdev->ops->get_station)
2617 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002618
Johannes Berg79c97e92009-07-07 03:56:12 +02002619 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002620 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002621 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002622
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002623 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002624 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002625 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002626
2627 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
John W. Linville66266b32012-03-15 13:25:41 -04002628 rdev, dev, mac_addr, &sinfo) < 0) {
Johannes Berg4c476992010-10-04 21:36:35 +02002629 nlmsg_free(msg);
2630 return -ENOBUFS;
2631 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002632
Johannes Berg4c476992010-10-04 21:36:35 +02002633 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002634}
2635
2636/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002637 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002638 */
Johannes Berg80b99892011-11-18 16:23:01 +01002639static struct net_device *get_vlan(struct genl_info *info,
2640 struct cfg80211_registered_device *rdev)
Johannes Berg5727ef12007-12-19 02:03:34 +01002641{
Johannes Berg463d0182009-07-14 00:33:35 +02002642 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg80b99892011-11-18 16:23:01 +01002643 struct net_device *v;
2644 int ret;
Johannes Berg5727ef12007-12-19 02:03:34 +01002645
Johannes Berg80b99892011-11-18 16:23:01 +01002646 if (!vlanattr)
2647 return NULL;
2648
2649 v = dev_get_by_index(genl_info_net(info), nla_get_u32(vlanattr));
2650 if (!v)
2651 return ERR_PTR(-ENODEV);
2652
2653 if (!v->ieee80211_ptr || v->ieee80211_ptr->wiphy != &rdev->wiphy) {
2654 ret = -EINVAL;
2655 goto error;
Johannes Berg5727ef12007-12-19 02:03:34 +01002656 }
Johannes Berg80b99892011-11-18 16:23:01 +01002657
2658 if (!netif_running(v)) {
2659 ret = -ENETDOWN;
2660 goto error;
2661 }
2662
2663 return v;
2664 error:
2665 dev_put(v);
2666 return ERR_PTR(ret);
Johannes Berg5727ef12007-12-19 02:03:34 +01002667}
2668
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002669static struct nla_policy
2670nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
2671 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
2672 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
2673};
2674
2675static int nl80211_set_station_tdls(struct genl_info *info,
2676 struct station_parameters *params)
2677{
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002678 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
2679 struct nlattr *nla;
2680 int err;
2681
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002682 /* Dummy STA entry gets updated once the peer capabilities are known */
2683 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2684 params->ht_capa =
2685 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2686 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
2687 params->vht_capa =
2688 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
2689
2690 /* parse WME attributes if present */
2691 if (!info->attrs[NL80211_ATTR_STA_WME])
2692 return 0;
2693
2694 nla = info->attrs[NL80211_ATTR_STA_WME];
2695 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
2696 nl80211_sta_wme_policy);
2697 if (err)
2698 return err;
2699
2700 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
2701 params->uapsd_queues = nla_get_u8(
2702 tb[NL80211_STA_WME_UAPSD_QUEUES]);
2703 if (params->uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
2704 return -EINVAL;
2705
2706 if (tb[NL80211_STA_WME_MAX_SP])
2707 params->max_sp = nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
2708
2709 if (params->max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
2710 return -EINVAL;
2711
2712 params->sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
2713
2714 return 0;
2715}
2716
Johannes Berg5727ef12007-12-19 02:03:34 +01002717static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2718{
Johannes Berg4c476992010-10-04 21:36:35 +02002719 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002720 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002721 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002722 struct station_parameters params;
2723 u8 *mac_addr = NULL;
2724
2725 memset(&params, 0, sizeof(params));
2726
2727 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002728 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002729
2730 if (info->attrs[NL80211_ATTR_STA_AID])
2731 return -EINVAL;
2732
2733 if (!info->attrs[NL80211_ATTR_MAC])
2734 return -EINVAL;
2735
2736 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2737
2738 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2739 params.supported_rates =
2740 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2741 params.supported_rates_len =
2742 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2743 }
2744
Jouni Malinen1400ced2013-02-14 19:10:13 +00002745 if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
2746 params.capability =
2747 nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
2748 params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
2749 }
2750
2751 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
2752 params.ext_capab =
2753 nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
2754 params.ext_capab_len =
2755 nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
2756 }
2757
Johannes Berg5727ef12007-12-19 02:03:34 +01002758 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2759 params.listen_interval =
2760 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2761
Jouni Malinen36aedc92008-08-25 11:58:58 +03002762 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2763 params.ht_capa =
2764 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2765
Johannes Bergbdd90d52011-12-14 12:20:27 +01002766 if (!rdev->ops->change_station)
2767 return -EOPNOTSUPP;
2768
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002769 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002770 return -EINVAL;
2771
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002772 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2773 params.plink_action =
2774 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2775
Javier Cardona9c3990a2011-05-03 16:57:11 -07002776 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2777 params.plink_state =
2778 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2779
Johannes Berga97f4422009-06-18 17:23:43 +02002780 switch (dev->ieee80211_ptr->iftype) {
2781 case NL80211_IFTYPE_AP:
2782 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002783 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002784 /* disallow mesh-specific things */
2785 if (params.plink_action)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002786 return -EINVAL;
2787
2788 /* TDLS can't be set, ... */
2789 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
2790 return -EINVAL;
2791 /*
2792 * ... but don't bother the driver with it. This works around
2793 * a hostapd/wpa_supplicant issue -- it always includes the
2794 * TLDS_PEER flag in the mask even for AP mode.
2795 */
2796 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
2797
2798 /* accept only the listed bits */
2799 if (params.sta_flags_mask &
2800 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2801 BIT(NL80211_STA_FLAG_SHORT_PREAMBLE) |
2802 BIT(NL80211_STA_FLAG_WME) |
2803 BIT(NL80211_STA_FLAG_MFP)))
2804 return -EINVAL;
2805
Jouni Malinen1400ced2013-02-14 19:10:13 +00002806 if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
2807 return -EINVAL;
2808 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
2809 return -EINVAL;
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002810 if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
2811 info->attrs[NL80211_ATTR_VHT_CAPABILITY])
2812 return -EINVAL;
Jouni Malinen1400ced2013-02-14 19:10:13 +00002813
Johannes Bergbdd90d52011-12-14 12:20:27 +01002814 /* must be last in here for error handling */
2815 params.vlan = get_vlan(info, rdev);
2816 if (IS_ERR(params.vlan))
2817 return PTR_ERR(params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002818 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002819 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002820 case NL80211_IFTYPE_STATION:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002821 /*
2822 * Don't allow userspace to change the TDLS_PEER flag,
2823 * but silently ignore attempts to change it since we
2824 * don't have state here to verify that it doesn't try
2825 * to change the flag.
2826 */
2827 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002828 /* Include parameters for TDLS peer (driver will check) */
2829 err = nl80211_set_station_tdls(info, &params);
2830 if (err)
2831 return err;
2832 /* disallow things sta doesn't support */
2833 if (params.plink_action)
2834 return -EINVAL;
2835 if (params.sta_flags_mask & ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2836 BIT(NL80211_STA_FLAG_WME)))
2837 return -EINVAL;
2838 break;
Antonio Quartulli267335d2012-01-31 20:25:47 +01002839 case NL80211_IFTYPE_ADHOC:
2840 /* disallow things sta doesn't support */
2841 if (params.plink_action)
2842 return -EINVAL;
2843 if (params.ht_capa)
2844 return -EINVAL;
2845 if (params.listen_interval >= 0)
2846 return -EINVAL;
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002847 if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
2848 info->attrs[NL80211_ATTR_VHT_CAPABILITY])
2849 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002850 /* reject any changes other than AUTHORIZED */
2851 if (params.sta_flags_mask & ~BIT(NL80211_STA_FLAG_AUTHORIZED))
2852 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002853 break;
2854 case NL80211_IFTYPE_MESH_POINT:
2855 /* disallow things mesh doesn't support */
2856 if (params.vlan)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002857 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002858 if (params.ht_capa)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002859 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002860 if (params.listen_interval >= 0)
Johannes Bergbdd90d52011-12-14 12:20:27 +01002861 return -EINVAL;
Jouni Malinen1400ced2013-02-14 19:10:13 +00002862 if (info->attrs[NL80211_ATTR_STA_CAPABILITY])
2863 return -EINVAL;
2864 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY])
2865 return -EINVAL;
Jouni Malinen95fcfd02013-02-14 19:10:54 +00002866 if (info->attrs[NL80211_ATTR_HT_CAPABILITY] ||
2867 info->attrs[NL80211_ATTR_VHT_CAPABILITY])
2868 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002869 /*
2870 * No special handling for TDLS here -- the userspace
2871 * mesh code doesn't have this bug.
2872 */
Javier Cardonab39c48f2011-04-07 15:08:30 -07002873 if (params.sta_flags_mask &
2874 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002875 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002876 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Bergbdd90d52011-12-14 12:20:27 +01002877 return -EINVAL;
Johannes Berga97f4422009-06-18 17:23:43 +02002878 break;
2879 default:
Johannes Bergbdd90d52011-12-14 12:20:27 +01002880 return -EOPNOTSUPP;
Johannes Berg034d6552009-05-27 10:35:29 +02002881 }
2882
Johannes Bergbdd90d52011-12-14 12:20:27 +01002883 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01002884
Johannes Berg79c97e92009-07-07 03:56:12 +02002885 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002886
Johannes Berg5727ef12007-12-19 02:03:34 +01002887 if (params.vlan)
2888 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002889
Johannes Berg5727ef12007-12-19 02:03:34 +01002890 return err;
2891}
2892
2893static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2894{
Johannes Berg4c476992010-10-04 21:36:35 +02002895 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002896 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002897 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002898 struct station_parameters params;
2899 u8 *mac_addr = NULL;
2900
2901 memset(&params, 0, sizeof(params));
2902
2903 if (!info->attrs[NL80211_ATTR_MAC])
2904 return -EINVAL;
2905
Johannes Berg5727ef12007-12-19 02:03:34 +01002906 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2907 return -EINVAL;
2908
2909 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2910 return -EINVAL;
2911
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002912 if (!info->attrs[NL80211_ATTR_STA_AID])
2913 return -EINVAL;
2914
Johannes Berg5727ef12007-12-19 02:03:34 +01002915 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2916 params.supported_rates =
2917 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2918 params.supported_rates_len =
2919 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2920 params.listen_interval =
2921 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002922
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002923 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2924 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2925 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002926
Jouni Malinen1400ced2013-02-14 19:10:13 +00002927 if (info->attrs[NL80211_ATTR_STA_CAPABILITY]) {
2928 params.capability =
2929 nla_get_u16(info->attrs[NL80211_ATTR_STA_CAPABILITY]);
2930 params.sta_modify_mask |= STATION_PARAM_APPLY_CAPABILITY;
2931 }
2932
2933 if (info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]) {
2934 params.ext_capab =
2935 nla_data(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
2936 params.ext_capab_len =
2937 nla_len(info->attrs[NL80211_ATTR_STA_EXT_CAPABILITY]);
2938 }
2939
Jouni Malinen36aedc92008-08-25 11:58:58 +03002940 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2941 params.ht_capa =
2942 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002943
Mahesh Palivelacf7f78d2012-10-11 08:04:52 +00002944 if (info->attrs[NL80211_ATTR_VHT_CAPABILITY])
2945 params.vht_capa =
2946 nla_data(info->attrs[NL80211_ATTR_VHT_CAPABILITY]);
2947
Javier Cardona96b78df2011-04-07 15:08:33 -07002948 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2949 params.plink_action =
2950 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2951
Johannes Bergbdd90d52011-12-14 12:20:27 +01002952 if (!rdev->ops->add_station)
2953 return -EOPNOTSUPP;
2954
Johannes Bergbdd3ae32012-01-02 13:30:03 +01002955 if (parse_station_flags(info, dev->ieee80211_ptr->iftype, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002956 return -EINVAL;
2957
Johannes Bergbdd90d52011-12-14 12:20:27 +01002958 switch (dev->ieee80211_ptr->iftype) {
2959 case NL80211_IFTYPE_AP:
2960 case NL80211_IFTYPE_AP_VLAN:
2961 case NL80211_IFTYPE_P2P_GO:
2962 /* parse WME attributes if sta is WME capable */
2963 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
2964 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
2965 info->attrs[NL80211_ATTR_STA_WME]) {
2966 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
2967 struct nlattr *nla;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002968
Johannes Bergbdd90d52011-12-14 12:20:27 +01002969 nla = info->attrs[NL80211_ATTR_STA_WME];
2970 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
2971 nl80211_sta_wme_policy);
2972 if (err)
2973 return err;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002974
Johannes Bergbdd90d52011-12-14 12:20:27 +01002975 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
2976 params.uapsd_queues =
2977 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
2978 if (params.uapsd_queues &
2979 ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
2980 return -EINVAL;
2981
2982 if (tb[NL80211_STA_WME_MAX_SP])
2983 params.max_sp =
2984 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
2985
2986 if (params.max_sp &
2987 ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
2988 return -EINVAL;
2989
2990 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
2991 }
2992 /* TDLS peers cannot be added */
2993 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02002994 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01002995 /* but don't bother the driver with it */
2996 params.sta_flags_mask &= ~BIT(NL80211_STA_FLAG_TDLS_PEER);
Eliad Pellerc75786c2011-08-23 14:37:46 +03002997
Johannes Bergbdd90d52011-12-14 12:20:27 +01002998 /* must be last in here for error handling */
2999 params.vlan = get_vlan(info, rdev);
3000 if (IS_ERR(params.vlan))
3001 return PTR_ERR(params.vlan);
3002 break;
3003 case NL80211_IFTYPE_MESH_POINT:
3004 /* TDLS peers cannot be added */
3005 if (params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER))
Johannes Berg4319e192011-09-07 11:50:48 +02003006 return -EINVAL;
Johannes Bergbdd90d52011-12-14 12:20:27 +01003007 break;
3008 case NL80211_IFTYPE_STATION:
Johannes Berg43e652a2013-03-04 08:29:46 +00003009 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergbdd90d52011-12-14 12:20:27 +01003010 /* Only TDLS peers can be added */
3011 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
3012 return -EINVAL;
3013 /* Can only add if TDLS ... */
3014 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS))
3015 return -EOPNOTSUPP;
3016 /* ... with external setup is supported */
3017 if (!(rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP))
3018 return -EOPNOTSUPP;
3019 break;
3020 default:
3021 return -EOPNOTSUPP;
Eliad Pellerc75786c2011-08-23 14:37:46 +03003022 }
3023
Johannes Bergbdd90d52011-12-14 12:20:27 +01003024 /* be aware of params.vlan when changing code here */
Johannes Berg5727ef12007-12-19 02:03:34 +01003025
Johannes Berg79c97e92009-07-07 03:56:12 +02003026 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01003027
Johannes Berg5727ef12007-12-19 02:03:34 +01003028 if (params.vlan)
3029 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01003030 return err;
3031}
3032
3033static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
3034{
Johannes Berg4c476992010-10-04 21:36:35 +02003035 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3036 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01003037 u8 *mac_addr = NULL;
3038
3039 if (info->attrs[NL80211_ATTR_MAC])
3040 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
3041
Johannes Berge80cf852009-05-11 14:43:13 +02003042 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02003043 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02003044 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02003045 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3046 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02003047
Johannes Berg4c476992010-10-04 21:36:35 +02003048 if (!rdev->ops->del_station)
3049 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01003050
Johannes Berg4c476992010-10-04 21:36:35 +02003051 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01003052}
3053
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003054static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
3055 int flags, struct net_device *dev,
3056 u8 *dst, u8 *next_hop,
3057 struct mpath_info *pinfo)
3058{
3059 void *hdr;
3060 struct nlattr *pinfoattr;
3061
3062 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
3063 if (!hdr)
3064 return -1;
3065
3066 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3067 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
3068 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
3069
Johannes Bergf5ea9122009-08-07 16:17:38 +02003070 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
3071
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003072 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
3073 if (!pinfoattr)
3074 goto nla_put_failure;
3075 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
3076 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
3077 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00003078 if (pinfo->filled & MPATH_INFO_SN)
3079 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
3080 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003081 if (pinfo->filled & MPATH_INFO_METRIC)
3082 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
3083 pinfo->metric);
3084 if (pinfo->filled & MPATH_INFO_EXPTIME)
3085 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
3086 pinfo->exptime);
3087 if (pinfo->filled & MPATH_INFO_FLAGS)
3088 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
3089 pinfo->flags);
3090 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
3091 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
3092 pinfo->discovery_timeout);
3093 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
3094 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
3095 pinfo->discovery_retries);
3096
3097 nla_nest_end(msg, pinfoattr);
3098
3099 return genlmsg_end(msg, hdr);
3100
3101 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07003102 genlmsg_cancel(msg, hdr);
3103 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003104}
3105
3106static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02003107 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003108{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003109 struct mpath_info pinfo;
3110 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003111 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003112 u8 dst[ETH_ALEN];
3113 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02003114 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003115 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003116
Johannes Berg67748892010-10-04 21:14:06 +02003117 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
3118 if (err)
3119 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003120
3121 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02003122 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003123 goto out_err;
3124 }
3125
Jouni Malineneec60b02009-03-20 21:21:19 +02003126 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
3127 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02003128 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02003129 }
3130
Johannes Bergbba95fe2008-07-29 13:22:51 +02003131 while (1) {
3132 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
3133 dst, next_hop, &pinfo);
3134 if (err == -ENOENT)
3135 break;
3136 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01003137 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003138
3139 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
3140 cb->nlh->nlmsg_seq, NLM_F_MULTI,
3141 netdev, dst, next_hop,
3142 &pinfo) < 0)
3143 goto out;
3144
3145 path_idx++;
3146 }
3147
3148
3149 out:
3150 cb->args[1] = path_idx;
3151 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02003152 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02003153 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02003154 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003155}
3156
3157static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
3158{
Johannes Berg4c476992010-10-04 21:36:35 +02003159 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003160 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02003161 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003162 struct mpath_info pinfo;
3163 struct sk_buff *msg;
3164 u8 *dst = NULL;
3165 u8 next_hop[ETH_ALEN];
3166
3167 memset(&pinfo, 0, sizeof(pinfo));
3168
3169 if (!info->attrs[NL80211_ATTR_MAC])
3170 return -EINVAL;
3171
3172 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3173
Johannes Berg4c476992010-10-04 21:36:35 +02003174 if (!rdev->ops->get_mpath)
3175 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003176
Johannes Berg4c476992010-10-04 21:36:35 +02003177 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3178 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003179
Johannes Berg79c97e92009-07-07 03:56:12 +02003180 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003181 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003182 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003183
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003184 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003185 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02003186 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003187
3188 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02003189 dev, dst, next_hop, &pinfo) < 0) {
3190 nlmsg_free(msg);
3191 return -ENOBUFS;
3192 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003193
Johannes Berg4c476992010-10-04 21:36:35 +02003194 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003195}
3196
3197static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
3198{
Johannes Berg4c476992010-10-04 21:36:35 +02003199 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3200 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003201 u8 *dst = NULL;
3202 u8 *next_hop = NULL;
3203
3204 if (!info->attrs[NL80211_ATTR_MAC])
3205 return -EINVAL;
3206
3207 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3208 return -EINVAL;
3209
3210 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3211 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3212
Johannes Berg4c476992010-10-04 21:36:35 +02003213 if (!rdev->ops->change_mpath)
3214 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003215
Johannes Berg4c476992010-10-04 21:36:35 +02003216 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3217 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003218
Johannes Berg4c476992010-10-04 21:36:35 +02003219 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003220}
Johannes Berg4c476992010-10-04 21:36:35 +02003221
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003222static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
3223{
Johannes Berg4c476992010-10-04 21:36:35 +02003224 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3225 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003226 u8 *dst = NULL;
3227 u8 *next_hop = NULL;
3228
3229 if (!info->attrs[NL80211_ATTR_MAC])
3230 return -EINVAL;
3231
3232 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
3233 return -EINVAL;
3234
3235 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3236 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
3237
Johannes Berg4c476992010-10-04 21:36:35 +02003238 if (!rdev->ops->add_mpath)
3239 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003240
Johannes Berg4c476992010-10-04 21:36:35 +02003241 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
3242 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003243
Johannes Berg4c476992010-10-04 21:36:35 +02003244 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003245}
3246
3247static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
3248{
Johannes Berg4c476992010-10-04 21:36:35 +02003249 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3250 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003251 u8 *dst = NULL;
3252
3253 if (info->attrs[NL80211_ATTR_MAC])
3254 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
3255
Johannes Berg4c476992010-10-04 21:36:35 +02003256 if (!rdev->ops->del_mpath)
3257 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01003258
Johannes Berg4c476992010-10-04 21:36:35 +02003259 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01003260}
3261
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003262static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
3263{
Johannes Berg4c476992010-10-04 21:36:35 +02003264 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3265 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003266 struct bss_parameters params;
3267
3268 memset(&params, 0, sizeof(params));
3269 /* default to not changing parameters */
3270 params.use_cts_prot = -1;
3271 params.use_short_preamble = -1;
3272 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003273 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01003274 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003275
3276 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
3277 params.use_cts_prot =
3278 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
3279 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
3280 params.use_short_preamble =
3281 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
3282 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
3283 params.use_short_slot_time =
3284 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02003285 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
3286 params.basic_rates =
3287 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3288 params.basic_rates_len =
3289 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3290 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003291 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
3292 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01003293 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
3294 params.ht_opmode =
3295 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003296
Johannes Berg4c476992010-10-04 21:36:35 +02003297 if (!rdev->ops->change_bss)
3298 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003299
Johannes Berg074ac8d2010-09-16 14:58:22 +02003300 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02003301 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3302 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003303
Johannes Berg4c476992010-10-04 21:36:35 +02003304 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003305}
3306
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003307static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003308 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
3309 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
3310 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
3311 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
3312 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
3313 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
3314};
3315
3316static int parse_reg_rule(struct nlattr *tb[],
3317 struct ieee80211_reg_rule *reg_rule)
3318{
3319 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
3320 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
3321
3322 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
3323 return -EINVAL;
3324 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
3325 return -EINVAL;
3326 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
3327 return -EINVAL;
3328 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
3329 return -EINVAL;
3330 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
3331 return -EINVAL;
3332
3333 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
3334
3335 freq_range->start_freq_khz =
3336 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
3337 freq_range->end_freq_khz =
3338 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
3339 freq_range->max_bandwidth_khz =
3340 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
3341
3342 power_rule->max_eirp =
3343 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
3344
3345 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
3346 power_rule->max_antenna_gain =
3347 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
3348
3349 return 0;
3350}
3351
3352static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3353{
3354 int r;
3355 char *data = NULL;
3356
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003357 /*
3358 * You should only get this when cfg80211 hasn't yet initialized
3359 * completely when built-in to the kernel right between the time
3360 * window between nl80211_init() and regulatory_init(), if that is
3361 * even possible.
3362 */
3363 mutex_lock(&cfg80211_mutex);
3364 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003365 mutex_unlock(&cfg80211_mutex);
3366 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003367 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003368 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003369
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003370 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3371 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003372
3373 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3374
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003375 r = regulatory_hint_user(data);
3376
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003377 return r;
3378}
3379
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003380static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003381 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003382{
Johannes Berg4c476992010-10-04 21:36:35 +02003383 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003384 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003385 struct wireless_dev *wdev = dev->ieee80211_ptr;
3386 struct mesh_config cur_params;
3387 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003388 void *hdr;
3389 struct nlattr *pinfoattr;
3390 struct sk_buff *msg;
3391
Johannes Berg29cbe682010-12-03 09:20:44 +01003392 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3393 return -EOPNOTSUPP;
3394
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003395 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02003396 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02003397
Johannes Berg29cbe682010-12-03 09:20:44 +01003398 wdev_lock(wdev);
3399 /* If not connected, get default parameters */
3400 if (!wdev->mesh_id_len)
3401 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
3402 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003403 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003404 &cur_params);
3405 wdev_unlock(wdev);
3406
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003407 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003408 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003409
3410 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003411 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003412 if (!msg)
3413 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003414 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003415 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003416 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003417 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003418 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003419 if (!pinfoattr)
3420 goto nla_put_failure;
3421 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3422 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
3423 cur_params.dot11MeshRetryTimeout);
3424 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3425 cur_params.dot11MeshConfirmTimeout);
3426 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
3427 cur_params.dot11MeshHoldingTimeout);
3428 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
3429 cur_params.dot11MeshMaxPeerLinks);
3430 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
3431 cur_params.dot11MeshMaxRetries);
3432 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
3433 cur_params.dot11MeshTTL);
Javier Cardona45904f22010-12-03 09:20:40 +01003434 NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL,
3435 cur_params.element_ttl);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003436 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3437 cur_params.auto_open_plinks);
3438 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3439 cur_params.dot11MeshHWMPmaxPREQretries);
3440 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3441 cur_params.path_refresh_time);
3442 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3443 cur_params.min_discovery_timeout);
3444 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3445 cur_params.dot11MeshHWMPactivePathTimeout);
3446 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3447 cur_params.dot11MeshHWMPpreqMinInterval);
Thomas Pedersendca7e942011-11-24 17:15:24 -08003448 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3449 cur_params.dot11MeshHWMPperrMinInterval);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003450 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3451 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00003452 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3453 cur_params.dot11MeshHWMPRootMode);
Javier Cardona0507e152011-08-09 16:45:10 -07003454 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3455 cur_params.dot11MeshHWMPRannInterval);
Javier Cardona16dd7262011-08-09 16:45:11 -07003456 NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3457 cur_params.dot11MeshGateAnnouncementProtocol);
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003458 NLA_PUT_U8(msg, NL80211_MESHCONF_FORWARDING,
3459 cur_params.dot11MeshForwarding);
Ashok Nagarajan55335132012-02-28 17:04:08 -08003460 NLA_PUT_U32(msg, NL80211_MESHCONF_RSSI_THRESHOLD,
3461 cur_params.rssi_threshold);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003462 nla_nest_end(msg, pinfoattr);
3463 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003464 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003465
Johannes Berg3b858752009-03-12 09:55:09 +01003466 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003467 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003468 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003469 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003470 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003471}
3472
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003473static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003474 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3475 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3476 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3477 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3478 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3479 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003480 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003481 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
3482
3483 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3484 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3485 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3486 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3487 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
Thomas Pedersendca7e942011-11-24 17:15:24 -08003488 [NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL] = { .type = NLA_U16 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003489 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07003490 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07003491 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07003492 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003493 [NL80211_MESHCONF_FORWARDING] = { .type = NLA_U8 },
Ashok Nagarajan55335132012-02-28 17:04:08 -08003494 [NL80211_MESHCONF_RSSI_THRESHOLD] = { .type = NLA_U32},
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003495};
3496
Javier Cardonac80d5452010-12-16 17:37:49 -08003497static const struct nla_policy
3498 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
3499 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3500 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003501 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003502 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Javier Cardonac80d5452010-12-16 17:37:49 -08003503 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003504 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003505};
3506
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003507static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003508 struct mesh_config *cfg,
3509 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003510{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003511 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003512 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003513
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003514#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3515do {\
3516 if (table[attr_num]) {\
3517 cfg->param = nla_fn(table[attr_num]); \
3518 mask |= (1 << (attr_num - 1)); \
3519 } \
3520} while (0);\
3521
3522
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003523 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003524 return -EINVAL;
3525 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003526 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003527 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003528 return -EINVAL;
3529
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003530 /* This makes sure that there aren't more than 32 mesh config
3531 * parameters (otherwise our bitfield scheme would not work.) */
3532 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3533
3534 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003535 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
3536 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
3537 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
3538 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
3539 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
3540 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
3541 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
3542 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
3543 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
3544 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
3545 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
3546 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003547 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
3548 mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003549 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
3550 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
3551 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
3552 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3553 nla_get_u8);
3554 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
3555 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
3556 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
3557 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3558 nla_get_u16);
3559 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
3560 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3561 nla_get_u32);
3562 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
3563 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3564 nla_get_u16);
Thomas Pedersendca7e942011-11-24 17:15:24 -08003565 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPperrMinInterval,
3566 mask, NL80211_MESHCONF_HWMP_PERR_MIN_INTERVAL,
3567 nla_get_u16);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003568 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3569 dot11MeshHWMPnetDiameterTraversalTime,
3570 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3571 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003572 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3573 dot11MeshHWMPRootMode, mask,
3574 NL80211_MESHCONF_HWMP_ROOTMODE,
3575 nla_get_u8);
Javier Cardona0507e152011-08-09 16:45:10 -07003576 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3577 dot11MeshHWMPRannInterval, mask,
3578 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3579 nla_get_u16);
Javier Cardona16dd7262011-08-09 16:45:11 -07003580 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3581 dot11MeshGateAnnouncementProtocol, mask,
3582 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3583 nla_get_u8);
Chun-Yeow Yeoh94f90652012-01-21 01:02:16 +08003584 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshForwarding,
3585 mask, NL80211_MESHCONF_FORWARDING, nla_get_u8);
Ashok Nagarajan55335132012-02-28 17:04:08 -08003586 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, rssi_threshold,
3587 mask, NL80211_MESHCONF_RSSI_THRESHOLD, nla_get_u32);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003588 if (mask_out)
3589 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003590
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003591 return 0;
3592
3593#undef FILL_IN_MESH_PARAM_IF_SET
3594}
3595
Javier Cardonac80d5452010-12-16 17:37:49 -08003596static int nl80211_parse_mesh_setup(struct genl_info *info,
3597 struct mesh_setup *setup)
3598{
3599 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3600
3601 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3602 return -EINVAL;
3603 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3604 info->attrs[NL80211_ATTR_MESH_SETUP],
3605 nl80211_mesh_setup_params_policy))
3606 return -EINVAL;
3607
3608 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3609 setup->path_sel_proto =
3610 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3611 IEEE80211_PATH_PROTOCOL_VENDOR :
3612 IEEE80211_PATH_PROTOCOL_HWMP;
3613
3614 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3615 setup->path_metric =
3616 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3617 IEEE80211_PATH_METRIC_VENDOR :
3618 IEEE80211_PATH_METRIC_AIRTIME;
3619
Javier Cardona581a8b02011-04-07 15:08:27 -07003620
3621 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003622 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003623 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003624 if (!is_valid_ie_attr(ieattr))
3625 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003626 setup->ie = nla_data(ieattr);
3627 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003628 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003629 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3630 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003631
3632 return 0;
3633}
3634
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003635static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003636 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003637{
3638 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3639 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003640 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003641 struct mesh_config cfg;
3642 u32 mask;
3643 int err;
3644
Johannes Berg29cbe682010-12-03 09:20:44 +01003645 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3646 return -EOPNOTSUPP;
3647
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003648 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003649 return -EOPNOTSUPP;
3650
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003651 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003652 if (err)
3653 return err;
3654
Johannes Berg29cbe682010-12-03 09:20:44 +01003655 wdev_lock(wdev);
3656 if (!wdev->mesh_id_len)
3657 err = -ENOLINK;
3658
3659 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003660 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003661 mask, &cfg);
3662
3663 wdev_unlock(wdev);
3664
3665 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003666}
3667
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003668static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3669{
3670 struct sk_buff *msg;
3671 void *hdr = NULL;
3672 struct nlattr *nl_reg_rules;
3673 unsigned int i;
3674 int err = -EINVAL;
3675
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003676 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003677
3678 if (!cfg80211_regdomain)
3679 goto out;
3680
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003681 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003682 if (!msg) {
3683 err = -ENOBUFS;
3684 goto out;
3685 }
3686
3687 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3688 NL80211_CMD_GET_REG);
3689 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003690 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003691
3692 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3693 cfg80211_regdomain->alpha2);
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003694 if (cfg80211_regdomain->dfs_region)
3695 NLA_PUT_U8(msg, NL80211_ATTR_DFS_REGION,
3696 cfg80211_regdomain->dfs_region);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003697
3698 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3699 if (!nl_reg_rules)
3700 goto nla_put_failure;
3701
3702 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3703 struct nlattr *nl_reg_rule;
3704 const struct ieee80211_reg_rule *reg_rule;
3705 const struct ieee80211_freq_range *freq_range;
3706 const struct ieee80211_power_rule *power_rule;
3707
3708 reg_rule = &cfg80211_regdomain->reg_rules[i];
3709 freq_range = &reg_rule->freq_range;
3710 power_rule = &reg_rule->power_rule;
3711
3712 nl_reg_rule = nla_nest_start(msg, i);
3713 if (!nl_reg_rule)
3714 goto nla_put_failure;
3715
3716 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3717 reg_rule->flags);
3718 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3719 freq_range->start_freq_khz);
3720 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3721 freq_range->end_freq_khz);
3722 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3723 freq_range->max_bandwidth_khz);
3724 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3725 power_rule->max_antenna_gain);
3726 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3727 power_rule->max_eirp);
3728
3729 nla_nest_end(msg, nl_reg_rule);
3730 }
3731
3732 nla_nest_end(msg, nl_reg_rules);
3733
3734 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003735 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003736 goto out;
3737
3738nla_put_failure:
3739 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003740put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003741 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003742 err = -EMSGSIZE;
3743out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003744 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003745 return err;
3746}
3747
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003748static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3749{
3750 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3751 struct nlattr *nl_reg_rule;
3752 char *alpha2 = NULL;
3753 int rem_reg_rules = 0, r = 0;
3754 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003755 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003756 struct ieee80211_regdomain *rd = NULL;
3757
3758 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3759 return -EINVAL;
3760
3761 if (!info->attrs[NL80211_ATTR_REG_RULES])
3762 return -EINVAL;
3763
3764 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3765
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003766 if (info->attrs[NL80211_ATTR_DFS_REGION])
3767 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
3768
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003769 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3770 rem_reg_rules) {
3771 num_rules++;
3772 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003773 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003774 }
3775
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003776 mutex_lock(&cfg80211_mutex);
3777
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003778 if (!reg_is_valid_request(alpha2)) {
3779 r = -EINVAL;
3780 goto bad_reg;
3781 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003782
3783 size_of_regd = sizeof(struct ieee80211_regdomain) +
3784 (num_rules * sizeof(struct ieee80211_reg_rule));
3785
3786 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003787 if (!rd) {
3788 r = -ENOMEM;
3789 goto bad_reg;
3790 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003791
3792 rd->n_reg_rules = num_rules;
3793 rd->alpha2[0] = alpha2[0];
3794 rd->alpha2[1] = alpha2[1];
3795
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003796 /*
3797 * Disable DFS master mode if the DFS region was
3798 * not supported or known on this kernel.
3799 */
3800 if (reg_supported_dfs_region(dfs_region))
3801 rd->dfs_region = dfs_region;
3802
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003803 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3804 rem_reg_rules) {
3805 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3806 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3807 reg_rule_policy);
3808 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3809 if (r)
3810 goto bad_reg;
3811
3812 rule_idx++;
3813
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003814 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3815 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003816 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003817 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003818 }
3819
3820 BUG_ON(rule_idx != num_rules);
3821
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003822 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003823
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003824 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003825
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003826 return r;
3827
Johannes Bergd2372b32008-10-24 20:32:20 +02003828 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003829 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003830 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003831 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003832}
3833
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003834static int validate_scan_freqs(struct nlattr *freqs)
3835{
3836 struct nlattr *attr1, *attr2;
3837 int n_channels = 0, tmp1, tmp2;
3838
3839 nla_for_each_nested(attr1, freqs, tmp1) {
3840 n_channels++;
3841 /*
3842 * Some hardware has a limited channel list for
3843 * scanning, and it is pretty much nonsensical
3844 * to scan for a channel twice, so disallow that
3845 * and don't require drivers to check that the
3846 * channel list they get isn't longer than what
3847 * they can scan, as long as they can scan all
3848 * the channels they registered at once.
3849 */
3850 nla_for_each_nested(attr2, freqs, tmp2)
3851 if (attr1 != attr2 &&
3852 nla_get_u32(attr1) == nla_get_u32(attr2))
3853 return 0;
3854 }
3855
3856 return n_channels;
3857}
3858
Johannes Berg2a519312009-02-10 21:25:55 +01003859static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3860{
Johannes Berg4c476992010-10-04 21:36:35 +02003861 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3862 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003863 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003864 struct nlattr *attr;
3865 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003866 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003867 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003868
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003869 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3870 return -EINVAL;
3871
Johannes Berg79c97e92009-07-07 03:56:12 +02003872 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003873
Johannes Berg4c476992010-10-04 21:36:35 +02003874 if (!rdev->ops->scan)
3875 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003876
Johannes Berg4c476992010-10-04 21:36:35 +02003877 if (rdev->scan_req)
3878 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003879
3880 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003881 n_channels = validate_scan_freqs(
3882 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003883 if (!n_channels)
3884 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003885 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003886 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003887 n_channels = 0;
3888
Johannes Berg2a519312009-02-10 21:25:55 +01003889 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3890 if (wiphy->bands[band])
3891 n_channels += wiphy->bands[band]->n_channels;
3892 }
3893
3894 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3895 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3896 n_ssids++;
3897
Johannes Berg4c476992010-10-04 21:36:35 +02003898 if (n_ssids > wiphy->max_scan_ssids)
3899 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003900
Jouni Malinen70692ad2009-02-16 19:39:13 +02003901 if (info->attrs[NL80211_ATTR_IE])
3902 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3903 else
3904 ie_len = 0;
3905
Johannes Berg4c476992010-10-04 21:36:35 +02003906 if (ie_len > wiphy->max_scan_ie_len)
3907 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003908
Johannes Berg2a519312009-02-10 21:25:55 +01003909 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003910 + sizeof(*request->ssids) * n_ssids
3911 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003912 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003913 if (!request)
3914 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003915
Johannes Berg2a519312009-02-10 21:25:55 +01003916 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003917 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003918 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003919 if (ie_len) {
3920 if (request->ssids)
3921 request->ie = (void *)(request->ssids + n_ssids);
3922 else
3923 request->ie = (void *)(request->channels + n_channels);
3924 }
Johannes Berg2a519312009-02-10 21:25:55 +01003925
Johannes Berg584991d2009-11-02 13:32:03 +01003926 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003927 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3928 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003929 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003930 struct ieee80211_channel *chan;
3931
3932 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3933
3934 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003935 err = -EINVAL;
3936 goto out_free;
3937 }
Johannes Berg584991d2009-11-02 13:32:03 +01003938
3939 /* ignore disabled channels */
3940 if (chan->flags & IEEE80211_CHAN_DISABLED)
3941 continue;
3942
3943 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003944 i++;
3945 }
3946 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003947 enum ieee80211_band band;
3948
Johannes Berg2a519312009-02-10 21:25:55 +01003949 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003950 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3951 int j;
3952 if (!wiphy->bands[band])
3953 continue;
3954 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003955 struct ieee80211_channel *chan;
3956
3957 chan = &wiphy->bands[band]->channels[j];
3958
3959 if (chan->flags & IEEE80211_CHAN_DISABLED)
3960 continue;
3961
3962 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003963 i++;
3964 }
3965 }
3966 }
3967
Johannes Berg584991d2009-11-02 13:32:03 +01003968 if (!i) {
3969 err = -EINVAL;
3970 goto out_free;
3971 }
3972
3973 request->n_channels = i;
3974
Johannes Berg2a519312009-02-10 21:25:55 +01003975 i = 0;
3976 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3977 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003978 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01003979 err = -EINVAL;
3980 goto out_free;
3981 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003982 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01003983 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01003984 i++;
3985 }
3986 }
3987
Jouni Malinen70692ad2009-02-16 19:39:13 +02003988 if (info->attrs[NL80211_ATTR_IE]) {
3989 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003990 memcpy((void *)request->ie,
3991 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003992 request->ie_len);
3993 }
Sunil Dutt Undekari08166fc2012-03-05 21:01:23 +05303994 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Sunil Dutt Undekariae86f142012-03-16 20:17:03 +05303995 if (wiphy->bands[i])
3996 request->rates[i] =
3997 (1 << wiphy->bands[i]->n_bitrates) - 1;
Sunil Dutt Undekari08166fc2012-03-05 21:01:23 +05303998 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
3999 nla_for_each_nested(attr,
4000 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
4001 tmp) {
4002 enum ieee80211_band band = nla_type(attr);
4003 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
4004 err = -EINVAL;
4005 goto out_free;
4006 }
4007 err = ieee80211_get_ratemask(wiphy->bands[band],
4008 nla_data(attr),
4009 nla_len(attr),
4010 &request->rates[band]);
4011 if (err)
4012 goto out_free;
4013 }
4014 }
Jouni Malinen70692ad2009-02-16 19:39:13 +02004015
Johannes Berg34850ab2011-07-18 18:08:35 +02004016 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02004017 if (wiphy->bands[i])
4018 request->rates[i] =
4019 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02004020
4021 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
4022 nla_for_each_nested(attr,
4023 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
4024 tmp) {
4025 enum ieee80211_band band = nla_type(attr);
4026
Dan Carpenter84404622011-07-29 11:52:18 +03004027 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02004028 err = -EINVAL;
4029 goto out_free;
4030 }
4031 err = ieee80211_get_ratemask(wiphy->bands[band],
4032 nla_data(attr),
4033 nla_len(attr),
4034 &request->rates[band]);
4035 if (err)
4036 goto out_free;
4037 }
4038 }
4039
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05304040 request->no_cck =
4041 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
4042
Johannes Berg463d0182009-07-14 00:33:35 +02004043 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02004044 request->wiphy = &rdev->wiphy;
Sunil Dutt Undekari08166fc2012-03-05 21:01:23 +05304045 request->no_cck =
4046 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
Johannes Berg2a519312009-02-10 21:25:55 +01004047
Johannes Berg79c97e92009-07-07 03:56:12 +02004048 rdev->scan_req = request;
4049 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01004050
Johannes Berg463d0182009-07-14 00:33:35 +02004051 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02004052 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02004053 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02004054 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01004055 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02004056 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01004057 kfree(request);
4058 }
Johannes Berg3b858752009-03-12 09:55:09 +01004059
Johannes Berg2a519312009-02-10 21:25:55 +01004060 return err;
4061}
4062
Luciano Coelho807f8a82011-05-11 17:09:35 +03004063static int nl80211_start_sched_scan(struct sk_buff *skb,
4064 struct genl_info *info)
4065{
4066 struct cfg80211_sched_scan_request *request;
4067 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4068 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004069 struct nlattr *attr;
4070 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004071 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004072 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004073 enum ieee80211_band band;
4074 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004075 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03004076
4077 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4078 !rdev->ops->sched_scan_start)
4079 return -EOPNOTSUPP;
4080
4081 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4082 return -EINVAL;
4083
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004084 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
4085 return -EINVAL;
4086
4087 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
4088 if (interval == 0)
4089 return -EINVAL;
4090
Luciano Coelho807f8a82011-05-11 17:09:35 +03004091 wiphy = &rdev->wiphy;
4092
4093 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4094 n_channels = validate_scan_freqs(
4095 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
4096 if (!n_channels)
4097 return -EINVAL;
4098 } else {
4099 n_channels = 0;
4100
4101 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
4102 if (wiphy->bands[band])
4103 n_channels += wiphy->bands[band]->n_channels;
4104 }
4105
4106 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
4107 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4108 tmp)
4109 n_ssids++;
4110
Luciano Coelho93b6aa62011-07-13 14:57:28 +03004111 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004112 return -EINVAL;
4113
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004114 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
4115 nla_for_each_nested(attr,
4116 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4117 tmp)
4118 n_match_sets++;
4119
4120 if (n_match_sets > wiphy->max_match_sets)
4121 return -EINVAL;
4122
Luciano Coelho807f8a82011-05-11 17:09:35 +03004123 if (info->attrs[NL80211_ATTR_IE])
4124 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4125 else
4126 ie_len = 0;
4127
Luciano Coelho5a865ba2011-07-13 14:57:29 +03004128 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03004129 return -EINVAL;
4130
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004131 mutex_lock(&rdev->sched_scan_mtx);
4132
4133 if (rdev->sched_scan_req) {
4134 err = -EINPROGRESS;
4135 goto out;
4136 }
4137
Luciano Coelho807f8a82011-05-11 17:09:35 +03004138 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004139 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004140 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03004141 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03004142 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004143 if (!request) {
4144 err = -ENOMEM;
4145 goto out;
4146 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03004147
4148 if (n_ssids)
4149 request->ssids = (void *)&request->channels[n_channels];
4150 request->n_ssids = n_ssids;
4151 if (ie_len) {
4152 if (request->ssids)
4153 request->ie = (void *)(request->ssids + n_ssids);
4154 else
4155 request->ie = (void *)(request->channels + n_channels);
4156 }
4157
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004158 if (n_match_sets) {
4159 if (request->ie)
4160 request->match_sets = (void *)(request->ie + ie_len);
4161 else if (request->ssids)
4162 request->match_sets =
4163 (void *)(request->ssids + n_ssids);
4164 else
4165 request->match_sets =
4166 (void *)(request->channels + n_channels);
4167 }
4168 request->n_match_sets = n_match_sets;
4169
Luciano Coelho807f8a82011-05-11 17:09:35 +03004170 i = 0;
4171 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
4172 /* user specified, bail out if channel not found */
4173 nla_for_each_nested(attr,
4174 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
4175 tmp) {
4176 struct ieee80211_channel *chan;
4177
4178 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
4179
4180 if (!chan) {
4181 err = -EINVAL;
4182 goto out_free;
4183 }
4184
4185 /* ignore disabled channels */
4186 if (chan->flags & IEEE80211_CHAN_DISABLED)
4187 continue;
4188
4189 request->channels[i] = chan;
4190 i++;
4191 }
4192 } else {
4193 /* all channels */
4194 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4195 int j;
4196 if (!wiphy->bands[band])
4197 continue;
4198 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
4199 struct ieee80211_channel *chan;
4200
4201 chan = &wiphy->bands[band]->channels[j];
4202
4203 if (chan->flags & IEEE80211_CHAN_DISABLED)
4204 continue;
4205
4206 request->channels[i] = chan;
4207 i++;
4208 }
4209 }
4210 }
4211
4212 if (!i) {
4213 err = -EINVAL;
4214 goto out_free;
4215 }
4216
4217 request->n_channels = i;
4218
4219 i = 0;
4220 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
4221 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
4222 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03004223 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03004224 err = -EINVAL;
4225 goto out_free;
4226 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03004227 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004228 memcpy(request->ssids[i].ssid, nla_data(attr),
4229 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03004230 i++;
4231 }
4232 }
4233
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03004234 i = 0;
4235 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
4236 nla_for_each_nested(attr,
4237 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
4238 tmp) {
4239 struct nlattr *ssid;
4240
4241 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
4242 nla_data(attr), nla_len(attr),
4243 nl80211_match_policy);
4244 ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID];
4245 if (ssid) {
4246 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
4247 err = -EINVAL;
4248 goto out_free;
4249 }
4250 memcpy(request->match_sets[i].ssid.ssid,
4251 nla_data(ssid), nla_len(ssid));
4252 request->match_sets[i].ssid.ssid_len =
4253 nla_len(ssid);
4254 }
4255 i++;
4256 }
4257 }
4258
Luciano Coelho807f8a82011-05-11 17:09:35 +03004259 if (info->attrs[NL80211_ATTR_IE]) {
4260 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4261 memcpy((void *)request->ie,
4262 nla_data(info->attrs[NL80211_ATTR_IE]),
4263 request->ie_len);
4264 }
4265
4266 request->dev = dev;
4267 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03004268 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004269
4270 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
4271 if (!err) {
4272 rdev->sched_scan_req = request;
4273 nl80211_send_sched_scan(rdev, dev,
4274 NL80211_CMD_START_SCHED_SCAN);
4275 goto out;
4276 }
4277
4278out_free:
4279 kfree(request);
4280out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004281 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03004282 return err;
4283}
4284
4285static int nl80211_stop_sched_scan(struct sk_buff *skb,
4286 struct genl_info *info)
4287{
4288 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004289 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004290
4291 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
4292 !rdev->ops->sched_scan_stop)
4293 return -EOPNOTSUPP;
4294
Luciano Coelhoc10841c2011-06-30 08:32:41 +03004295 mutex_lock(&rdev->sched_scan_mtx);
4296 err = __cfg80211_stop_sched_scan(rdev, false);
4297 mutex_unlock(&rdev->sched_scan_mtx);
4298
4299 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03004300}
4301
Johannes Berg9720bb32011-06-21 09:45:33 +02004302static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
4303 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004304 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02004305 struct wireless_dev *wdev,
4306 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01004307{
Johannes Berg48ab9052009-07-10 18:42:31 +02004308 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01004309 void *hdr;
4310 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02004311
4312 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004313
Johannes Berg9720bb32011-06-21 09:45:33 +02004314 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01004315 NL80211_CMD_NEW_SCAN_RESULTS);
4316 if (!hdr)
4317 return -1;
4318
Johannes Berg9720bb32011-06-21 09:45:33 +02004319 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
4320
Johannes Bergf5ea9122009-08-07 16:17:38 +02004321 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02004322 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01004323
4324 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
4325 if (!bss)
4326 goto nla_put_failure;
4327 if (!is_zero_ether_addr(res->bssid))
4328 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
4329 if (res->information_elements && res->len_information_elements)
4330 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
4331 res->len_information_elements,
4332 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02004333 if (res->beacon_ies && res->len_beacon_ies &&
4334 res->beacon_ies != res->information_elements)
4335 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
4336 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01004337 if (res->tsf)
4338 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
4339 if (res->beacon_interval)
4340 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
4341 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
4342 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02004343 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
4344 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01004345
Johannes Berg77965c92009-02-18 18:45:06 +01004346 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01004347 case CFG80211_SIGNAL_TYPE_MBM:
4348 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
4349 break;
4350 case CFG80211_SIGNAL_TYPE_UNSPEC:
4351 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
4352 break;
4353 default:
4354 break;
4355 }
4356
Johannes Berg48ab9052009-07-10 18:42:31 +02004357 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02004358 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02004359 case NL80211_IFTYPE_STATION:
4360 if (intbss == wdev->current_bss)
4361 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4362 NL80211_BSS_STATUS_ASSOCIATED);
Johannes Berg48ab9052009-07-10 18:42:31 +02004363 break;
4364 case NL80211_IFTYPE_ADHOC:
4365 if (intbss == wdev->current_bss)
4366 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4367 NL80211_BSS_STATUS_IBSS_JOINED);
4368 break;
4369 default:
4370 break;
4371 }
4372
Johannes Berg2a519312009-02-10 21:25:55 +01004373 nla_nest_end(msg, bss);
4374
4375 return genlmsg_end(msg, hdr);
4376
4377 nla_put_failure:
4378 genlmsg_cancel(msg, hdr);
4379 return -EMSGSIZE;
4380}
4381
4382static int nl80211_dump_scan(struct sk_buff *skb,
4383 struct netlink_callback *cb)
4384{
Johannes Berg48ab9052009-07-10 18:42:31 +02004385 struct cfg80211_registered_device *rdev;
4386 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01004387 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02004388 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01004389 int start = cb->args[1], idx = 0;
4390 int err;
4391
Johannes Berg67748892010-10-04 21:14:06 +02004392 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
4393 if (err)
4394 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01004395
Johannes Berg48ab9052009-07-10 18:42:31 +02004396 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01004397
Johannes Berg48ab9052009-07-10 18:42:31 +02004398 wdev_lock(wdev);
4399 spin_lock_bh(&rdev->bss_lock);
4400 cfg80211_bss_expire(rdev);
4401
Johannes Berg9720bb32011-06-21 09:45:33 +02004402 cb->seq = rdev->bss_generation;
4403
Johannes Berg48ab9052009-07-10 18:42:31 +02004404 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01004405 if (++idx <= start)
4406 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02004407 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01004408 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02004409 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004410 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02004411 break;
Johannes Berg2a519312009-02-10 21:25:55 +01004412 }
4413 }
4414
Johannes Berg48ab9052009-07-10 18:42:31 +02004415 spin_unlock_bh(&rdev->bss_lock);
4416 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004417
4418 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02004419 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004420
Johannes Berg67748892010-10-04 21:14:06 +02004421 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01004422}
4423
Holger Schurig61fa7132009-11-11 12:25:40 +01004424static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
4425 int flags, struct net_device *dev,
4426 struct survey_info *survey)
4427{
4428 void *hdr;
4429 struct nlattr *infoattr;
4430
Holger Schurig61fa7132009-11-11 12:25:40 +01004431 hdr = nl80211hdr_put(msg, pid, seq, flags,
4432 NL80211_CMD_NEW_SURVEY_RESULTS);
4433 if (!hdr)
4434 return -ENOMEM;
4435
4436 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
4437
4438 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
4439 if (!infoattr)
4440 goto nla_put_failure;
4441
4442 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
4443 survey->channel->center_freq);
4444 if (survey->filled & SURVEY_INFO_NOISE_DBM)
4445 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
4446 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02004447 if (survey->filled & SURVEY_INFO_IN_USE)
4448 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Felix Fietkau8610c29a2010-10-09 02:39:29 +02004449 if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
4450 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
4451 survey->channel_time);
4452 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
4453 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
4454 survey->channel_time_busy);
4455 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
4456 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
4457 survey->channel_time_ext_busy);
4458 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
4459 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
4460 survey->channel_time_rx);
4461 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
4462 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
4463 survey->channel_time_tx);
Holger Schurig61fa7132009-11-11 12:25:40 +01004464
4465 nla_nest_end(msg, infoattr);
4466
4467 return genlmsg_end(msg, hdr);
4468
4469 nla_put_failure:
4470 genlmsg_cancel(msg, hdr);
4471 return -EMSGSIZE;
4472}
4473
4474static int nl80211_dump_survey(struct sk_buff *skb,
4475 struct netlink_callback *cb)
4476{
4477 struct survey_info survey;
4478 struct cfg80211_registered_device *dev;
4479 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01004480 int survey_idx = cb->args[1];
4481 int res;
4482
Johannes Berg67748892010-10-04 21:14:06 +02004483 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
4484 if (res)
4485 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01004486
4487 if (!dev->ops->dump_survey) {
4488 res = -EOPNOTSUPP;
4489 goto out_err;
4490 }
4491
4492 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004493 struct ieee80211_channel *chan;
4494
Holger Schurig61fa7132009-11-11 12:25:40 +01004495 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
4496 &survey);
4497 if (res == -ENOENT)
4498 break;
4499 if (res)
4500 goto out_err;
4501
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004502 /* Survey without a channel doesn't make sense */
4503 if (!survey.channel) {
4504 res = -EINVAL;
4505 goto out;
4506 }
4507
4508 chan = ieee80211_get_channel(&dev->wiphy,
4509 survey.channel->center_freq);
4510 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
4511 survey_idx++;
4512 continue;
4513 }
4514
Holger Schurig61fa7132009-11-11 12:25:40 +01004515 if (nl80211_send_survey(skb,
4516 NETLINK_CB(cb->skb).pid,
4517 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4518 netdev,
4519 &survey) < 0)
4520 goto out;
4521 survey_idx++;
4522 }
4523
4524 out:
4525 cb->args[1] = survey_idx;
4526 res = skb->len;
4527 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02004528 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01004529 return res;
4530}
4531
Jouni Malinen255e7372009-03-20 21:21:17 +02004532static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
4533{
Samuel Ortizb23aa672009-07-01 21:26:54 +02004534 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02004535}
4536
Samuel Ortizb23aa672009-07-01 21:26:54 +02004537static bool nl80211_valid_wpa_versions(u32 wpa_versions)
4538{
4539 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
4540 NL80211_WPA_VERSION_2));
4541}
4542
Jouni Malinen636a5d32009-03-19 13:39:22 +02004543static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
4544{
Johannes Berg4c476992010-10-04 21:36:35 +02004545 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4546 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004547 struct ieee80211_channel *chan;
4548 const u8 *bssid, *ssid, *ie = NULL;
4549 int err, ssid_len, ie_len = 0;
4550 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02004551 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004552 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004553
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004554 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4555 return -EINVAL;
4556
4557 if (!info->attrs[NL80211_ATTR_MAC])
4558 return -EINVAL;
4559
Jouni Malinen17780922009-03-27 20:52:47 +02004560 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4561 return -EINVAL;
4562
Johannes Berg19957bb2009-07-02 17:20:43 +02004563 if (!info->attrs[NL80211_ATTR_SSID])
4564 return -EINVAL;
4565
4566 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4567 return -EINVAL;
4568
Johannes Bergfffd0932009-07-08 14:22:54 +02004569 err = nl80211_parse_key(info, &key);
4570 if (err)
4571 return err;
4572
4573 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004574 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4575 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004576 if (!key.p.key || !key.p.key_len)
4577 return -EINVAL;
4578 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4579 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4580 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4581 key.p.key_len != WLAN_KEY_LEN_WEP104))
4582 return -EINVAL;
4583 if (key.idx > 4)
4584 return -EINVAL;
4585 } else {
4586 key.p.key_len = 0;
4587 key.p.key = NULL;
4588 }
4589
Johannes Bergafea0b72010-08-10 09:46:42 +02004590 if (key.idx >= 0) {
4591 int i;
4592 bool ok = false;
4593 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4594 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4595 ok = true;
4596 break;
4597 }
4598 }
Johannes Berg4c476992010-10-04 21:36:35 +02004599 if (!ok)
4600 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004601 }
4602
Johannes Berg4c476992010-10-04 21:36:35 +02004603 if (!rdev->ops->auth)
4604 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004605
Johannes Berg074ac8d2010-09-16 14:58:22 +02004606 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004607 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4608 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004609
Johannes Berg19957bb2009-07-02 17:20:43 +02004610 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004611 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004612 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004613 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4614 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004615
Johannes Berg19957bb2009-07-02 17:20:43 +02004616 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4617 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4618
4619 if (info->attrs[NL80211_ATTR_IE]) {
4620 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4621 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4622 }
4623
4624 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004625 if (!nl80211_valid_auth_type(auth_type))
4626 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004627
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004628 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4629
Johannes Berg95de8172012-01-20 13:55:25 +01004630 /*
4631 * Since we no longer track auth state, ignore
4632 * requests to only change local state.
4633 */
4634 if (local_state_change)
4635 return 0;
4636
Johannes Berg4c476992010-10-04 21:36:35 +02004637 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4638 ssid, ssid_len, ie, ie_len,
Johannes Berg95de8172012-01-20 13:55:25 +01004639 key.p.key, key.p.key_len, key.idx);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004640}
4641
Johannes Bergc0692b82010-08-27 14:26:53 +03004642static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4643 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004644 struct cfg80211_crypto_settings *settings,
4645 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004646{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004647 memset(settings, 0, sizeof(*settings));
4648
Samuel Ortizb23aa672009-07-01 21:26:54 +02004649 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4650
Johannes Bergc0692b82010-08-27 14:26:53 +03004651 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4652 u16 proto;
4653 proto = nla_get_u16(
4654 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4655 settings->control_port_ethertype = cpu_to_be16(proto);
4656 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4657 proto != ETH_P_PAE)
4658 return -EINVAL;
4659 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4660 settings->control_port_no_encrypt = true;
4661 } else
4662 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4663
Samuel Ortizb23aa672009-07-01 21:26:54 +02004664 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4665 void *data;
4666 int len, i;
4667
4668 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4669 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4670 settings->n_ciphers_pairwise = len / sizeof(u32);
4671
4672 if (len % sizeof(u32))
4673 return -EINVAL;
4674
Johannes Berg3dc27d22009-07-02 21:36:37 +02004675 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004676 return -EINVAL;
4677
4678 memcpy(settings->ciphers_pairwise, data, len);
4679
4680 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004681 if (!cfg80211_supported_cipher_suite(
4682 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004683 settings->ciphers_pairwise[i]))
4684 return -EINVAL;
4685 }
4686
4687 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4688 settings->cipher_group =
4689 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004690 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
4691 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02004692 return -EINVAL;
4693 }
4694
4695 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4696 settings->wpa_versions =
4697 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4698 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4699 return -EINVAL;
4700 }
4701
4702 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4703 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03004704 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004705
4706 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4707 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4708 settings->n_akm_suites = len / sizeof(u32);
4709
4710 if (len % sizeof(u32))
4711 return -EINVAL;
4712
Jouni Malinen1b9ca022011-09-21 16:13:07 +03004713 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4714 return -EINVAL;
4715
Samuel Ortizb23aa672009-07-01 21:26:54 +02004716 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004717 }
4718
4719 return 0;
4720}
4721
Jouni Malinen636a5d32009-03-19 13:39:22 +02004722static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4723{
Johannes Berg4c476992010-10-04 21:36:35 +02004724 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4725 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004726 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004727 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004728 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004729 int err, ssid_len, ie_len = 0;
4730 bool use_mfp = false;
Ben Greear7e7c8922011-11-18 11:31:59 -08004731 u32 flags = 0;
4732 struct ieee80211_ht_cap *ht_capa = NULL;
4733 struct ieee80211_ht_cap *ht_capa_mask = NULL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004734
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004735 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4736 return -EINVAL;
4737
4738 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004739 !info->attrs[NL80211_ATTR_SSID] ||
4740 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004741 return -EINVAL;
4742
Johannes Berg4c476992010-10-04 21:36:35 +02004743 if (!rdev->ops->assoc)
4744 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004745
Johannes Berg074ac8d2010-09-16 14:58:22 +02004746 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004747 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4748 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004749
Johannes Berg19957bb2009-07-02 17:20:43 +02004750 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004751
Johannes Berg19957bb2009-07-02 17:20:43 +02004752 chan = ieee80211_get_channel(&rdev->wiphy,
4753 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004754 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4755 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004756
Johannes Berg19957bb2009-07-02 17:20:43 +02004757 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4758 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004759
4760 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004761 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4762 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004763 }
4764
Jouni Malinendc6382c2009-05-06 22:09:37 +03004765 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004766 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004767 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004768 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004769 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004770 else if (mfp != NL80211_MFP_NO)
4771 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004772 }
4773
Johannes Berg3e5d7642009-07-07 14:37:26 +02004774 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4775 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4776
Ben Greear7e7c8922011-11-18 11:31:59 -08004777 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
4778 flags |= ASSOC_REQ_DISABLE_HT;
4779
4780 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
4781 ht_capa_mask =
4782 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]);
4783
4784 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
4785 if (!ht_capa_mask)
4786 return -EINVAL;
4787 ht_capa = nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
4788 }
4789
Johannes Bergc0692b82010-08-27 14:26:53 +03004790 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004791 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004792 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4793 ssid, ssid_len, ie, ie_len, use_mfp,
Ben Greear7e7c8922011-11-18 11:31:59 -08004794 &crypto, flags, ht_capa,
4795 ht_capa_mask);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004796
Jouni Malinen636a5d32009-03-19 13:39:22 +02004797 return err;
4798}
4799
4800static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4801{
Johannes Berg4c476992010-10-04 21:36:35 +02004802 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4803 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004804 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004805 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004806 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004807 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004808
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004809 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4810 return -EINVAL;
4811
4812 if (!info->attrs[NL80211_ATTR_MAC])
4813 return -EINVAL;
4814
4815 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4816 return -EINVAL;
4817
Johannes Berg4c476992010-10-04 21:36:35 +02004818 if (!rdev->ops->deauth)
4819 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004820
Johannes Berg074ac8d2010-09-16 14:58:22 +02004821 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004822 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4823 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004824
Johannes Berg19957bb2009-07-02 17:20:43 +02004825 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004826
Johannes Berg19957bb2009-07-02 17:20:43 +02004827 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4828 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004829 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004830 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004831 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004832
4833 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004834 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4835 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004836 }
4837
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004838 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4839
Johannes Berg4c476992010-10-04 21:36:35 +02004840 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4841 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004842}
4843
4844static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4845{
Johannes Berg4c476992010-10-04 21:36:35 +02004846 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4847 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004848 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004849 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004850 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004851 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004852
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004853 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4854 return -EINVAL;
4855
4856 if (!info->attrs[NL80211_ATTR_MAC])
4857 return -EINVAL;
4858
4859 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4860 return -EINVAL;
4861
Johannes Berg4c476992010-10-04 21:36:35 +02004862 if (!rdev->ops->disassoc)
4863 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004864
Johannes Berg074ac8d2010-09-16 14:58:22 +02004865 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004866 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4867 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004868
Johannes Berg19957bb2009-07-02 17:20:43 +02004869 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004870
Johannes Berg19957bb2009-07-02 17:20:43 +02004871 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4872 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004873 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004874 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004875 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004876
4877 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004878 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4879 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004880 }
4881
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004882 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4883
Johannes Berg4c476992010-10-04 21:36:35 +02004884 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4885 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004886}
4887
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004888static bool
4889nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4890 int mcast_rate[IEEE80211_NUM_BANDS],
4891 int rateval)
4892{
4893 struct wiphy *wiphy = &rdev->wiphy;
4894 bool found = false;
4895 int band, i;
4896
4897 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4898 struct ieee80211_supported_band *sband;
4899
4900 sband = wiphy->bands[band];
4901 if (!sband)
4902 continue;
4903
4904 for (i = 0; i < sband->n_bitrates; i++) {
4905 if (sband->bitrates[i].bitrate == rateval) {
4906 mcast_rate[band] = i + 1;
4907 found = true;
4908 break;
4909 }
4910 }
4911 }
4912
4913 return found;
4914}
4915
Johannes Berg04a773a2009-04-19 21:24:32 +02004916static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4917{
Johannes Berg4c476992010-10-04 21:36:35 +02004918 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4919 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004920 struct cfg80211_ibss_params ibss;
4921 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004922 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004923 int err;
4924
Johannes Berg8e30bc52009-04-22 17:45:38 +02004925 memset(&ibss, 0, sizeof(ibss));
4926
Johannes Berg04a773a2009-04-19 21:24:32 +02004927 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4928 return -EINVAL;
4929
4930 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4931 !info->attrs[NL80211_ATTR_SSID] ||
4932 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4933 return -EINVAL;
4934
Johannes Berg8e30bc52009-04-22 17:45:38 +02004935 ibss.beacon_interval = 100;
4936
4937 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4938 ibss.beacon_interval =
4939 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4940 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4941 return -EINVAL;
4942 }
4943
Johannes Berg4c476992010-10-04 21:36:35 +02004944 if (!rdev->ops->join_ibss)
4945 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004946
Johannes Berg4c476992010-10-04 21:36:35 +02004947 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4948 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004949
Johannes Berg79c97e92009-07-07 03:56:12 +02004950 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004951
Johannes Berg39193492011-09-16 13:45:25 +02004952 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02004953 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02004954
4955 if (!is_valid_ether_addr(ibss.bssid))
4956 return -EINVAL;
4957 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004958 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4959 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4960
4961 if (info->attrs[NL80211_ATTR_IE]) {
4962 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4963 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4964 }
4965
Alexander Simon54858ee2011-11-30 16:56:32 +01004966 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
4967 enum nl80211_channel_type channel_type;
4968
4969 channel_type = nla_get_u32(
4970 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
4971 if (channel_type != NL80211_CHAN_NO_HT &&
4972 channel_type != NL80211_CHAN_HT20 &&
4973 channel_type != NL80211_CHAN_HT40MINUS &&
4974 channel_type != NL80211_CHAN_HT40PLUS)
4975 return -EINVAL;
4976
4977 if (channel_type != NL80211_CHAN_NO_HT &&
4978 !(wiphy->features & NL80211_FEATURE_HT_IBSS))
4979 return -EINVAL;
4980
4981 ibss.channel_type = channel_type;
4982 } else {
4983 ibss.channel_type = NL80211_CHAN_NO_HT;
4984 }
4985
4986 ibss.channel = rdev_freq_to_chan(rdev,
4987 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]),
4988 ibss.channel_type);
Johannes Berg04a773a2009-04-19 21:24:32 +02004989 if (!ibss.channel ||
4990 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004991 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4992 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004993
Alexander Simon54858ee2011-11-30 16:56:32 +01004994 /* Both channels should be able to initiate communication */
4995 if ((ibss.channel_type == NL80211_CHAN_HT40PLUS ||
4996 ibss.channel_type == NL80211_CHAN_HT40MINUS) &&
4997 !cfg80211_can_beacon_sec_chan(&rdev->wiphy, ibss.channel,
4998 ibss.channel_type))
4999 return -EINVAL;
5000
Johannes Berg04a773a2009-04-19 21:24:32 +02005001 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02005002 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02005003
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005004 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
5005 u8 *rates =
5006 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5007 int n_rates =
5008 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
5009 struct ieee80211_supported_band *sband =
5010 wiphy->bands[ibss.channel->band];
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005011
Johannes Berg34850ab2011-07-18 18:08:35 +02005012 err = ieee80211_get_ratemask(sband, rates, n_rates,
5013 &ibss.basic_rates);
5014 if (err)
5015 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005016 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01005017
5018 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
5019 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
5020 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
5021 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03005022
Johannes Berg4c476992010-10-04 21:36:35 +02005023 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5024 connkeys = nl80211_parse_connkeys(rdev,
5025 info->attrs[NL80211_ATTR_KEYS]);
5026 if (IS_ERR(connkeys))
5027 return PTR_ERR(connkeys);
5028 }
Johannes Berg04a773a2009-04-19 21:24:32 +02005029
Antonio Quartulli267335d2012-01-31 20:25:47 +01005030 ibss.control_port =
5031 nla_get_flag(info->attrs[NL80211_ATTR_CONTROL_PORT]);
5032
Johannes Berg4c476992010-10-04 21:36:35 +02005033 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005034 if (err)
5035 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02005036 return err;
5037}
5038
5039static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
5040{
Johannes Berg4c476992010-10-04 21:36:35 +02005041 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5042 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02005043
Johannes Berg4c476992010-10-04 21:36:35 +02005044 if (!rdev->ops->leave_ibss)
5045 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005046
Johannes Berg4c476992010-10-04 21:36:35 +02005047 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
5048 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02005049
Johannes Berg4c476992010-10-04 21:36:35 +02005050 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02005051}
5052
Johannes Bergaff89a92009-07-01 21:26:51 +02005053#ifdef CONFIG_NL80211_TESTMODE
5054static struct genl_multicast_group nl80211_testmode_mcgrp = {
5055 .name = "testmode",
5056};
5057
5058static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
5059{
Johannes Berg4c476992010-10-04 21:36:35 +02005060 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02005061 int err;
5062
5063 if (!info->attrs[NL80211_ATTR_TESTDATA])
5064 return -EINVAL;
5065
Johannes Bergaff89a92009-07-01 21:26:51 +02005066 err = -EOPNOTSUPP;
5067 if (rdev->ops->testmode_cmd) {
5068 rdev->testmode_info = info;
5069 err = rdev->ops->testmode_cmd(&rdev->wiphy,
5070 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
5071 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
5072 rdev->testmode_info = NULL;
5073 }
5074
Johannes Bergaff89a92009-07-01 21:26:51 +02005075 return err;
5076}
5077
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005078static int nl80211_testmode_dump(struct sk_buff *skb,
5079 struct netlink_callback *cb)
5080{
Johannes Berg00918d32011-12-13 17:22:05 +01005081 struct cfg80211_registered_device *rdev;
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005082 int err;
5083 long phy_idx;
5084 void *data = NULL;
5085 int data_len = 0;
5086
5087 if (cb->args[0]) {
5088 /*
5089 * 0 is a valid index, but not valid for args[0],
5090 * so we need to offset by 1.
5091 */
5092 phy_idx = cb->args[0] - 1;
5093 } else {
5094 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
5095 nl80211_fam.attrbuf, nl80211_fam.maxattr,
5096 nl80211_policy);
5097 if (err)
5098 return err;
Johannes Berg00918d32011-12-13 17:22:05 +01005099 if (nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]) {
5100 phy_idx = nla_get_u32(
5101 nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
5102 } else {
5103 struct net_device *netdev;
5104
5105 err = get_rdev_dev_by_ifindex(sock_net(skb->sk),
5106 nl80211_fam.attrbuf,
5107 &rdev, &netdev);
5108 if (err)
5109 return err;
5110 dev_put(netdev);
5111 phy_idx = rdev->wiphy_idx;
5112 cfg80211_unlock_rdev(rdev);
5113 }
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005114 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
5115 cb->args[1] =
5116 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
5117 }
5118
5119 if (cb->args[1]) {
5120 data = nla_data((void *)cb->args[1]);
5121 data_len = nla_len((void *)cb->args[1]);
5122 }
5123
5124 mutex_lock(&cfg80211_mutex);
Johannes Berg00918d32011-12-13 17:22:05 +01005125 rdev = cfg80211_rdev_by_wiphy_idx(phy_idx);
5126 if (!rdev) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005127 mutex_unlock(&cfg80211_mutex);
5128 return -ENOENT;
5129 }
Johannes Berg00918d32011-12-13 17:22:05 +01005130 cfg80211_lock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005131 mutex_unlock(&cfg80211_mutex);
5132
Johannes Berg00918d32011-12-13 17:22:05 +01005133 if (!rdev->ops->testmode_dump) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005134 err = -EOPNOTSUPP;
5135 goto out_err;
5136 }
5137
5138 while (1) {
5139 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
5140 cb->nlh->nlmsg_seq, NLM_F_MULTI,
5141 NL80211_CMD_TESTMODE);
5142 struct nlattr *tmdata;
5143
Johannes Berg00918d32011-12-13 17:22:05 +01005144 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx) < 0) {
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005145 genlmsg_cancel(skb, hdr);
5146 break;
5147 }
5148
5149 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5150 if (!tmdata) {
5151 genlmsg_cancel(skb, hdr);
5152 break;
5153 }
Johannes Berg00918d32011-12-13 17:22:05 +01005154 err = rdev->ops->testmode_dump(&rdev->wiphy, skb, cb,
5155 data, data_len);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005156 nla_nest_end(skb, tmdata);
5157
5158 if (err == -ENOBUFS || err == -ENOENT) {
5159 genlmsg_cancel(skb, hdr);
5160 break;
5161 } else if (err) {
5162 genlmsg_cancel(skb, hdr);
5163 goto out_err;
5164 }
5165
5166 genlmsg_end(skb, hdr);
5167 }
5168
5169 err = skb->len;
5170 /* see above */
5171 cb->args[0] = phy_idx + 1;
5172 out_err:
Johannes Berg00918d32011-12-13 17:22:05 +01005173 cfg80211_unlock_rdev(rdev);
Wey-Yi Guy71063f02011-05-20 09:05:54 -07005174 return err;
5175}
5176
Johannes Bergaff89a92009-07-01 21:26:51 +02005177static struct sk_buff *
5178__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
5179 int approxlen, u32 pid, u32 seq, gfp_t gfp)
5180{
5181 struct sk_buff *skb;
5182 void *hdr;
5183 struct nlattr *data;
5184
5185 skb = nlmsg_new(approxlen + 100, gfp);
5186 if (!skb)
5187 return NULL;
5188
5189 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
5190 if (!hdr) {
5191 kfree_skb(skb);
5192 return NULL;
5193 }
5194
5195 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
5196 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
5197
5198 ((void **)skb->cb)[0] = rdev;
5199 ((void **)skb->cb)[1] = hdr;
5200 ((void **)skb->cb)[2] = data;
5201
5202 return skb;
5203
5204 nla_put_failure:
5205 kfree_skb(skb);
5206 return NULL;
5207}
5208
5209struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
5210 int approxlen)
5211{
5212 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5213
5214 if (WARN_ON(!rdev->testmode_info))
5215 return NULL;
5216
5217 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
5218 rdev->testmode_info->snd_pid,
5219 rdev->testmode_info->snd_seq,
5220 GFP_KERNEL);
5221}
5222EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
5223
5224int cfg80211_testmode_reply(struct sk_buff *skb)
5225{
5226 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
5227 void *hdr = ((void **)skb->cb)[1];
5228 struct nlattr *data = ((void **)skb->cb)[2];
5229
5230 if (WARN_ON(!rdev->testmode_info)) {
5231 kfree_skb(skb);
5232 return -EINVAL;
5233 }
5234
5235 nla_nest_end(skb, data);
5236 genlmsg_end(skb, hdr);
5237 return genlmsg_reply(skb, rdev->testmode_info);
5238}
5239EXPORT_SYMBOL(cfg80211_testmode_reply);
5240
5241struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
5242 int approxlen, gfp_t gfp)
5243{
5244 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
5245
5246 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
5247}
5248EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
5249
5250void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
5251{
5252 void *hdr = ((void **)skb->cb)[1];
5253 struct nlattr *data = ((void **)skb->cb)[2];
5254
5255 nla_nest_end(skb, data);
5256 genlmsg_end(skb, hdr);
5257 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
5258}
5259EXPORT_SYMBOL(cfg80211_testmode_event);
5260#endif
5261
Samuel Ortizb23aa672009-07-01 21:26:54 +02005262static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
5263{
Johannes Berg4c476992010-10-04 21:36:35 +02005264 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5265 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005266 struct cfg80211_connect_params connect;
5267 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02005268 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005269 int err;
5270
5271 memset(&connect, 0, sizeof(connect));
5272
5273 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
5274 return -EINVAL;
5275
5276 if (!info->attrs[NL80211_ATTR_SSID] ||
5277 !nla_len(info->attrs[NL80211_ATTR_SSID]))
5278 return -EINVAL;
5279
5280 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
5281 connect.auth_type =
5282 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
5283 if (!nl80211_valid_auth_type(connect.auth_type))
5284 return -EINVAL;
5285 } else
5286 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
5287
5288 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
5289
Johannes Bergc0692b82010-08-27 14:26:53 +03005290 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02005291 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005292 if (err)
5293 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005294
Johannes Berg074ac8d2010-09-16 14:58:22 +02005295 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005296 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5297 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005298
Johannes Berg79c97e92009-07-07 03:56:12 +02005299 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005300
Bala Shanmugam4486ea92012-03-07 17:27:12 +05305301 connect.bg_scan_period = -1;
5302 if (info->attrs[NL80211_ATTR_BG_SCAN_PERIOD] &&
5303 (wiphy->flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)) {
5304 connect.bg_scan_period =
5305 nla_get_u16(info->attrs[NL80211_ATTR_BG_SCAN_PERIOD]);
5306 }
5307
Samuel Ortizb23aa672009-07-01 21:26:54 +02005308 if (info->attrs[NL80211_ATTR_MAC])
5309 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5310 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
5311 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
5312
5313 if (info->attrs[NL80211_ATTR_IE]) {
5314 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
5315 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
5316 }
5317
Jouni Malinen7e73ad52013-01-15 15:15:57 +00005318 if (info->attrs[NL80211_ATTR_USE_MFP]) {
5319 connect.mfp = nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
5320 if (connect.mfp != NL80211_MFP_REQUIRED &&
5321 connect.mfp != NL80211_MFP_NO)
5322 return -EINVAL;
5323 } else {
5324 connect.mfp = NL80211_MFP_NO;
5325 }
5326
Samuel Ortizb23aa672009-07-01 21:26:54 +02005327 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
5328 connect.channel =
5329 ieee80211_get_channel(wiphy,
5330 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
5331 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02005332 connect.channel->flags & IEEE80211_CHAN_DISABLED)
5333 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005334 }
5335
Johannes Bergfffd0932009-07-08 14:22:54 +02005336 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
5337 connkeys = nl80211_parse_connkeys(rdev,
5338 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02005339 if (IS_ERR(connkeys))
5340 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005341 }
5342
Ben Greear7e7c8922011-11-18 11:31:59 -08005343 if (nla_get_flag(info->attrs[NL80211_ATTR_DISABLE_HT]))
5344 connect.flags |= ASSOC_REQ_DISABLE_HT;
5345
5346 if (info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5347 memcpy(&connect.ht_capa_mask,
5348 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK]),
5349 sizeof(connect.ht_capa_mask));
5350
5351 if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) {
5352 if (!info->attrs[NL80211_ATTR_HT_CAPABILITY_MASK])
5353 return -EINVAL;
5354 memcpy(&connect.ht_capa,
5355 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]),
5356 sizeof(connect.ht_capa));
5357 }
5358
Johannes Bergfffd0932009-07-08 14:22:54 +02005359 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02005360 if (err)
5361 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005362 return err;
5363}
5364
5365static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
5366{
Johannes Berg4c476992010-10-04 21:36:35 +02005367 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5368 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02005369 u16 reason;
5370
5371 if (!info->attrs[NL80211_ATTR_REASON_CODE])
5372 reason = WLAN_REASON_DEAUTH_LEAVING;
5373 else
5374 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
5375
5376 if (reason == 0)
5377 return -EINVAL;
5378
Johannes Berg074ac8d2010-09-16 14:58:22 +02005379 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005380 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5381 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02005382
Johannes Berg4c476992010-10-04 21:36:35 +02005383 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02005384}
5385
Johannes Berg463d0182009-07-14 00:33:35 +02005386static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
5387{
Johannes Berg4c476992010-10-04 21:36:35 +02005388 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02005389 struct net *net;
5390 int err;
5391 u32 pid;
5392
5393 if (!info->attrs[NL80211_ATTR_PID])
5394 return -EINVAL;
5395
5396 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
5397
Johannes Berg463d0182009-07-14 00:33:35 +02005398 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02005399 if (IS_ERR(net))
5400 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005401
5402 err = 0;
5403
5404 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02005405 if (!net_eq(wiphy_net(&rdev->wiphy), net))
5406 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02005407
Johannes Berg463d0182009-07-14 00:33:35 +02005408 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02005409 return err;
5410}
5411
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005412static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
5413{
Johannes Berg4c476992010-10-04 21:36:35 +02005414 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005415 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
5416 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02005417 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005418 struct cfg80211_pmksa pmksa;
5419
5420 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
5421
5422 if (!info->attrs[NL80211_ATTR_MAC])
5423 return -EINVAL;
5424
5425 if (!info->attrs[NL80211_ATTR_PMKID])
5426 return -EINVAL;
5427
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005428 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
5429 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5430
Johannes Berg074ac8d2010-09-16 14:58:22 +02005431 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005432 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5433 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005434
5435 switch (info->genlhdr->cmd) {
5436 case NL80211_CMD_SET_PMKSA:
5437 rdev_ops = rdev->ops->set_pmksa;
5438 break;
5439 case NL80211_CMD_DEL_PMKSA:
5440 rdev_ops = rdev->ops->del_pmksa;
5441 break;
5442 default:
5443 WARN_ON(1);
5444 break;
5445 }
5446
Johannes Berg4c476992010-10-04 21:36:35 +02005447 if (!rdev_ops)
5448 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005449
Johannes Berg4c476992010-10-04 21:36:35 +02005450 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005451}
5452
5453static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
5454{
Johannes Berg4c476992010-10-04 21:36:35 +02005455 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5456 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005457
Johannes Berg074ac8d2010-09-16 14:58:22 +02005458 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005459 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5460 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005461
Johannes Berg4c476992010-10-04 21:36:35 +02005462 if (!rdev->ops->flush_pmksa)
5463 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005464
Johannes Berg4c476992010-10-04 21:36:35 +02005465 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005466}
5467
Arik Nemtsov109086c2011-09-28 14:12:50 +03005468static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
5469{
5470 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5471 struct net_device *dev = info->user_ptr[1];
5472 u8 action_code, dialog_token;
5473 u16 status_code;
5474 u8 *peer;
5475
5476 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5477 !rdev->ops->tdls_mgmt)
5478 return -EOPNOTSUPP;
5479
5480 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
5481 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
5482 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
5483 !info->attrs[NL80211_ATTR_IE] ||
5484 !info->attrs[NL80211_ATTR_MAC])
5485 return -EINVAL;
5486
5487 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5488 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
5489 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
5490 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
5491
5492 return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
5493 dialog_token, status_code,
5494 nla_data(info->attrs[NL80211_ATTR_IE]),
5495 nla_len(info->attrs[NL80211_ATTR_IE]));
5496}
5497
5498static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
5499{
5500 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5501 struct net_device *dev = info->user_ptr[1];
5502 enum nl80211_tdls_operation operation;
5503 u8 *peer;
5504
5505 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5506 !rdev->ops->tdls_oper)
5507 return -EOPNOTSUPP;
5508
5509 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
5510 !info->attrs[NL80211_ATTR_MAC])
5511 return -EINVAL;
5512
5513 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
5514 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5515
5516 return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
5517}
5518
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005519static int nl80211_remain_on_channel(struct sk_buff *skb,
5520 struct genl_info *info)
5521{
Johannes Berg4c476992010-10-04 21:36:35 +02005522 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5523 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005524 struct ieee80211_channel *chan;
5525 struct sk_buff *msg;
5526 void *hdr;
5527 u64 cookie;
5528 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
5529 u32 freq, duration;
5530 int err;
5531
5532 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5533 !info->attrs[NL80211_ATTR_DURATION])
5534 return -EINVAL;
5535
5536 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5537
5538 /*
5539 * We should be on that channel for at least one jiffie,
5540 * and more than 5 seconds seems excessive.
5541 */
Johannes Berga2939112010-12-14 17:54:28 +01005542 if (!duration || !msecs_to_jiffies(duration) ||
5543 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005544 return -EINVAL;
5545
Johannes Berg7c4ef712011-11-18 15:33:48 +01005546 if (!rdev->ops->remain_on_channel ||
5547 !(rdev->wiphy.flags & WIPHY_FLAG_HAS_REMAIN_ON_CHANNEL))
Johannes Berg4c476992010-10-04 21:36:35 +02005548 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005549
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005550 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5551 channel_type = nla_get_u32(
5552 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5553 if (channel_type != NL80211_CHAN_NO_HT &&
5554 channel_type != NL80211_CHAN_HT20 &&
5555 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005556 channel_type != NL80211_CHAN_HT40MINUS)
5557 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005558 }
5559
5560 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5561 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005562 if (chan == NULL)
5563 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005564
5565 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005566 if (!msg)
5567 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005568
5569 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5570 NL80211_CMD_REMAIN_ON_CHANNEL);
5571
5572 if (IS_ERR(hdr)) {
5573 err = PTR_ERR(hdr);
5574 goto free_msg;
5575 }
5576
5577 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
5578 channel_type, duration, &cookie);
5579
5580 if (err)
5581 goto free_msg;
5582
5583 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5584
5585 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005586
5587 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005588
5589 nla_put_failure:
5590 err = -ENOBUFS;
5591 free_msg:
5592 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005593 return err;
5594}
5595
5596static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
5597 struct genl_info *info)
5598{
Johannes Berg4c476992010-10-04 21:36:35 +02005599 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5600 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005601 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005602
5603 if (!info->attrs[NL80211_ATTR_COOKIE])
5604 return -EINVAL;
5605
Johannes Berg4c476992010-10-04 21:36:35 +02005606 if (!rdev->ops->cancel_remain_on_channel)
5607 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005608
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005609 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5610
Johannes Berg4c476992010-10-04 21:36:35 +02005611 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005612}
5613
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005614static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
5615 u8 *rates, u8 rates_len)
5616{
5617 u8 i;
5618 u32 mask = 0;
5619
5620 for (i = 0; i < rates_len; i++) {
5621 int rate = (rates[i] & 0x7f) * 5;
5622 int ridx;
5623 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
5624 struct ieee80211_rate *srate =
5625 &sband->bitrates[ridx];
5626 if (rate == srate->bitrate) {
5627 mask |= 1 << ridx;
5628 break;
5629 }
5630 }
5631 if (ridx == sband->n_bitrates)
5632 return 0; /* rate not found */
5633 }
5634
5635 return mask;
5636}
5637
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005638static bool ht_rateset_to_mask(struct ieee80211_supported_band *sband,
5639 u8 *rates, u8 rates_len,
5640 u8 mcs[IEEE80211_HT_MCS_MASK_LEN])
5641{
5642 u8 i;
5643
5644 memset(mcs, 0, IEEE80211_HT_MCS_MASK_LEN);
5645
5646 for (i = 0; i < rates_len; i++) {
5647 int ridx, rbit;
5648
5649 ridx = rates[i] / 8;
5650 rbit = BIT(rates[i] % 8);
5651
5652 /* check validity */
Dan Carpenter910570b2012-02-01 10:42:11 +03005653 if ((ridx < 0) || (ridx >= IEEE80211_HT_MCS_MASK_LEN))
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005654 return false;
5655
5656 /* check availability */
5657 if (sband->ht_cap.mcs.rx_mask[ridx] & rbit)
5658 mcs[ridx] |= rbit;
5659 else
5660 return false;
5661 }
5662
5663 return true;
5664}
5665
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00005666static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005667 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
5668 .len = NL80211_MAX_SUPP_RATES },
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005669 [NL80211_TXRATE_MCS] = { .type = NLA_BINARY,
5670 .len = NL80211_MAX_SUPP_HT_RATES },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005671};
5672
5673static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
5674 struct genl_info *info)
5675{
5676 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02005677 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005678 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02005679 int rem, i;
5680 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005681 struct nlattr *tx_rates;
5682 struct ieee80211_supported_band *sband;
5683
5684 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
5685 return -EINVAL;
5686
Johannes Berg4c476992010-10-04 21:36:35 +02005687 if (!rdev->ops->set_bitrate_mask)
5688 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005689
5690 memset(&mask, 0, sizeof(mask));
5691 /* Default to all rates enabled */
5692 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
5693 sband = rdev->wiphy.bands[i];
5694 mask.control[i].legacy =
5695 sband ? (1 << sband->n_bitrates) - 1 : 0;
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005696 if (sband)
5697 memcpy(mask.control[i].mcs,
5698 sband->ht_cap.mcs.rx_mask,
5699 sizeof(mask.control[i].mcs));
5700 else
5701 memset(mask.control[i].mcs, 0,
5702 sizeof(mask.control[i].mcs));
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005703 }
5704
5705 /*
5706 * The nested attribute uses enum nl80211_band as the index. This maps
5707 * directly to the enum ieee80211_band values used in cfg80211.
5708 */
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005709 BUILD_BUG_ON(NL80211_MAX_SUPP_HT_RATES > IEEE80211_HT_MCS_MASK_LEN * 8);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005710 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
5711 {
5712 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02005713 if (band < 0 || band >= IEEE80211_NUM_BANDS)
5714 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005715 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02005716 if (sband == NULL)
5717 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005718 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
5719 nla_len(tx_rates), nl80211_txattr_policy);
5720 if (tb[NL80211_TXRATE_LEGACY]) {
5721 mask.control[band].legacy = rateset_to_mask(
5722 sband,
5723 nla_data(tb[NL80211_TXRATE_LEGACY]),
5724 nla_len(tb[NL80211_TXRATE_LEGACY]));
Simon Wunderlich24db78c2012-01-28 17:25:32 +01005725 }
5726 if (tb[NL80211_TXRATE_MCS]) {
5727 if (!ht_rateset_to_mask(
5728 sband,
5729 nla_data(tb[NL80211_TXRATE_MCS]),
5730 nla_len(tb[NL80211_TXRATE_MCS]),
5731 mask.control[band].mcs))
5732 return -EINVAL;
5733 }
5734
5735 if (mask.control[band].legacy == 0) {
5736 /* don't allow empty legacy rates if HT
5737 * is not even supported. */
5738 if (!rdev->wiphy.bands[band]->ht_cap.ht_supported)
5739 return -EINVAL;
5740
5741 for (i = 0; i < IEEE80211_HT_MCS_MASK_LEN; i++)
5742 if (mask.control[band].mcs[i])
5743 break;
5744
5745 /* legacy and mcs rates may not be both empty */
5746 if (i == IEEE80211_HT_MCS_MASK_LEN)
Johannes Berg4c476992010-10-04 21:36:35 +02005747 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005748 }
5749 }
5750
Johannes Berg4c476992010-10-04 21:36:35 +02005751 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005752}
5753
Johannes Berg2e161f72010-08-12 15:38:38 +02005754static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005755{
Johannes Berg4c476992010-10-04 21:36:35 +02005756 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5757 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02005758 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02005759
5760 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
5761 return -EINVAL;
5762
Johannes Berg2e161f72010-08-12 15:38:38 +02005763 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
5764 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02005765
Johannes Berg9d38d852010-06-09 17:20:33 +02005766 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005767 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005768 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5769 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5770 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005771 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005772 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5773 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005774
5775 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005776 if (!rdev->ops->mgmt_tx)
5777 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005778
Johannes Berg4c476992010-10-04 21:36:35 +02005779 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02005780 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005781 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5782 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005783}
5784
Johannes Berg2e161f72010-08-12 15:38:38 +02005785static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005786{
Johannes Berg4c476992010-10-04 21:36:35 +02005787 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5788 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005789 struct ieee80211_channel *chan;
5790 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005791 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005792 u32 freq;
5793 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01005794 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005795 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01005796 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005797 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01005798 bool offchan, no_cck, dont_wait_for_ack;
5799
5800 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02005801
5802 if (!info->attrs[NL80211_ATTR_FRAME] ||
5803 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5804 return -EINVAL;
5805
Johannes Berg4c476992010-10-04 21:36:35 +02005806 if (!rdev->ops->mgmt_tx)
5807 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005808
Johannes Berg9d38d852010-06-09 17:20:33 +02005809 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005810 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005811 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5812 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5813 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005814 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005815 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5816 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005817
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005818 if (info->attrs[NL80211_ATTR_DURATION]) {
Johannes Berg7c4ef712011-11-18 15:33:48 +01005819 if (!(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005820 return -EINVAL;
5821 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5822 }
5823
Jouni Malinen026331c2010-02-15 12:53:10 +02005824 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5825 channel_type = nla_get_u32(
5826 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5827 if (channel_type != NL80211_CHAN_NO_HT &&
5828 channel_type != NL80211_CHAN_HT20 &&
5829 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005830 channel_type != NL80211_CHAN_HT40MINUS)
5831 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005832 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005833 }
5834
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005835 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5836
Johannes Berg7c4ef712011-11-18 15:33:48 +01005837 if (offchan && !(rdev->wiphy.flags & WIPHY_FLAG_OFFCHAN_TX))
5838 return -EINVAL;
5839
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305840 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5841
Jouni Malinen026331c2010-02-15 12:53:10 +02005842 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5843 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005844 if (chan == NULL)
5845 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005846
Johannes Berge247bd902011-11-04 11:18:21 +01005847 if (!dont_wait_for_ack) {
5848 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5849 if (!msg)
5850 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005851
Johannes Berge247bd902011-11-04 11:18:21 +01005852 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5853 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005854
Johannes Berge247bd902011-11-04 11:18:21 +01005855 if (IS_ERR(hdr)) {
5856 err = PTR_ERR(hdr);
5857 goto free_msg;
5858 }
Jouni Malinen026331c2010-02-15 12:53:10 +02005859 }
Johannes Berge247bd902011-11-04 11:18:21 +01005860
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005861 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5862 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02005863 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5864 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01005865 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005866 if (err)
5867 goto free_msg;
5868
Johannes Berge247bd902011-11-04 11:18:21 +01005869 if (msg) {
5870 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005871
Johannes Berge247bd902011-11-04 11:18:21 +01005872 genlmsg_end(msg, hdr);
5873 return genlmsg_reply(msg, info);
5874 }
5875
5876 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02005877
5878 nla_put_failure:
5879 err = -ENOBUFS;
5880 free_msg:
5881 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005882 return err;
5883}
5884
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005885static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5886{
5887 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5888 struct net_device *dev = info->user_ptr[1];
5889 u64 cookie;
5890
5891 if (!info->attrs[NL80211_ATTR_COOKIE])
5892 return -EINVAL;
5893
5894 if (!rdev->ops->mgmt_tx_cancel_wait)
5895 return -EOPNOTSUPP;
5896
5897 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5898 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5899 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5900 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5901 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5902 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5903 return -EOPNOTSUPP;
5904
5905 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5906
5907 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5908}
5909
Kalle Valoffb9eb32010-02-17 17:58:10 +02005910static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5911{
Johannes Berg4c476992010-10-04 21:36:35 +02005912 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005913 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005914 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005915 u8 ps_state;
5916 bool state;
5917 int err;
5918
Johannes Berg4c476992010-10-04 21:36:35 +02005919 if (!info->attrs[NL80211_ATTR_PS_STATE])
5920 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005921
5922 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5923
Johannes Berg4c476992010-10-04 21:36:35 +02005924 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5925 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005926
5927 wdev = dev->ieee80211_ptr;
5928
Johannes Berg4c476992010-10-04 21:36:35 +02005929 if (!rdev->ops->set_power_mgmt)
5930 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005931
5932 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5933
5934 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005935 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005936
Johannes Berg4c476992010-10-04 21:36:35 +02005937 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5938 wdev->ps_timeout);
5939 if (!err)
5940 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005941 return err;
5942}
5943
5944static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5945{
Johannes Berg4c476992010-10-04 21:36:35 +02005946 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005947 enum nl80211_ps_state ps_state;
5948 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005949 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005950 struct sk_buff *msg;
5951 void *hdr;
5952 int err;
5953
Kalle Valoffb9eb32010-02-17 17:58:10 +02005954 wdev = dev->ieee80211_ptr;
5955
Johannes Berg4c476992010-10-04 21:36:35 +02005956 if (!rdev->ops->set_power_mgmt)
5957 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005958
5959 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005960 if (!msg)
5961 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005962
5963 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5964 NL80211_CMD_GET_POWER_SAVE);
5965 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005966 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005967 goto free_msg;
5968 }
5969
5970 if (wdev->ps)
5971 ps_state = NL80211_PS_ENABLED;
5972 else
5973 ps_state = NL80211_PS_DISABLED;
5974
5975 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5976
5977 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005978 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005979
Johannes Berg4c476992010-10-04 21:36:35 +02005980 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005981 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005982 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005983 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005984 return err;
5985}
5986
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005987static struct nla_policy
5988nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5989 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5990 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5991 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5992};
5993
5994static int nl80211_set_cqm_rssi(struct genl_info *info,
5995 s32 threshold, u32 hysteresis)
5996{
Johannes Berg4c476992010-10-04 21:36:35 +02005997 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005998 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005999 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006000
6001 if (threshold > 0)
6002 return -EINVAL;
6003
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006004 wdev = dev->ieee80211_ptr;
6005
Johannes Berg4c476992010-10-04 21:36:35 +02006006 if (!rdev->ops->set_cqm_rssi_config)
6007 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006008
Johannes Berg074ac8d2010-09-16 14:58:22 +02006009 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02006010 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
6011 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006012
Johannes Berg4c476992010-10-04 21:36:35 +02006013 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
6014 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006015}
6016
6017static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
6018{
6019 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
6020 struct nlattr *cqm;
6021 int err;
6022
6023 cqm = info->attrs[NL80211_ATTR_CQM];
6024 if (!cqm) {
6025 err = -EINVAL;
6026 goto out;
6027 }
6028
6029 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
6030 nl80211_attr_cqm_policy);
6031 if (err)
6032 goto out;
6033
6034 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
6035 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
6036 s32 threshold;
6037 u32 hysteresis;
6038 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
6039 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
6040 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
6041 } else
6042 err = -EINVAL;
6043
6044out:
6045 return err;
6046}
6047
Johannes Berg29cbe682010-12-03 09:20:44 +01006048static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
6049{
6050 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6051 struct net_device *dev = info->user_ptr[1];
6052 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08006053 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01006054 int err;
6055
6056 /* start with default */
6057 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08006058 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01006059
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006060 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01006061 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006062 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01006063 if (err)
6064 return err;
6065 }
6066
6067 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
6068 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
6069 return -EINVAL;
6070
Javier Cardonac80d5452010-12-16 17:37:49 -08006071 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
6072 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
6073
Chun-Yeow Yeoh4bb62342011-11-24 17:15:20 -08006074 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
6075 !nl80211_parse_mcast_rate(rdev, setup.mcast_rate,
6076 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
6077 return -EINVAL;
6078
Javier Cardonac80d5452010-12-16 17:37:49 -08006079 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
6080 /* parse additional setup parameters if given */
6081 err = nl80211_parse_mesh_setup(info, &setup);
6082 if (err)
6083 return err;
6084 }
6085
6086 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01006087}
6088
6089static int nl80211_leave_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
6094 return cfg80211_leave_mesh(rdev, dev);
6095}
6096
Johannes Bergff1b6e62011-05-04 15:37:28 +02006097static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
6098{
6099 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6100 struct sk_buff *msg;
6101 void *hdr;
6102
6103 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6104 return -EOPNOTSUPP;
6105
6106 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6107 if (!msg)
6108 return -ENOMEM;
6109
6110 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6111 NL80211_CMD_GET_WOWLAN);
6112 if (!hdr)
6113 goto nla_put_failure;
6114
6115 if (rdev->wowlan) {
6116 struct nlattr *nl_wowlan;
6117
6118 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
6119 if (!nl_wowlan)
6120 goto nla_put_failure;
6121
6122 if (rdev->wowlan->any)
6123 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
6124 if (rdev->wowlan->disconnect)
6125 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
6126 if (rdev->wowlan->magic_pkt)
6127 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02006128 if (rdev->wowlan->gtk_rekey_failure)
6129 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
6130 if (rdev->wowlan->eap_identity_req)
6131 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
6132 if (rdev->wowlan->four_way_handshake)
6133 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
6134 if (rdev->wowlan->rfkill_release)
6135 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02006136 if (rdev->wowlan->n_patterns) {
6137 struct nlattr *nl_pats, *nl_pat;
6138 int i, pat_len;
6139
6140 nl_pats = nla_nest_start(msg,
6141 NL80211_WOWLAN_TRIG_PKT_PATTERN);
6142 if (!nl_pats)
6143 goto nla_put_failure;
6144
6145 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
6146 nl_pat = nla_nest_start(msg, i + 1);
6147 if (!nl_pat)
6148 goto nla_put_failure;
6149 pat_len = rdev->wowlan->patterns[i].pattern_len;
6150 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
6151 DIV_ROUND_UP(pat_len, 8),
6152 rdev->wowlan->patterns[i].mask);
6153 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
6154 pat_len,
6155 rdev->wowlan->patterns[i].pattern);
6156 nla_nest_end(msg, nl_pat);
6157 }
6158 nla_nest_end(msg, nl_pats);
6159 }
6160
6161 nla_nest_end(msg, nl_wowlan);
6162 }
6163
6164 genlmsg_end(msg, hdr);
6165 return genlmsg_reply(msg, info);
6166
6167nla_put_failure:
6168 nlmsg_free(msg);
6169 return -ENOBUFS;
6170}
6171
6172static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
6173{
6174 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6175 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
6176 struct cfg80211_wowlan no_triggers = {};
6177 struct cfg80211_wowlan new_triggers = {};
6178 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
6179 int err, i;
6180
6181 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
6182 return -EOPNOTSUPP;
6183
6184 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
6185 goto no_triggers;
6186
6187 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
6188 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6189 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
6190 nl80211_wowlan_policy);
6191 if (err)
6192 return err;
6193
6194 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
6195 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
6196 return -EINVAL;
6197 new_triggers.any = true;
6198 }
6199
6200 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
6201 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
6202 return -EINVAL;
6203 new_triggers.disconnect = true;
6204 }
6205
6206 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
6207 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
6208 return -EINVAL;
6209 new_triggers.magic_pkt = true;
6210 }
6211
Johannes Berg77dbbb12011-07-13 10:48:55 +02006212 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
6213 return -EINVAL;
6214
6215 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
6216 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
6217 return -EINVAL;
6218 new_triggers.gtk_rekey_failure = true;
6219 }
6220
6221 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
6222 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
6223 return -EINVAL;
6224 new_triggers.eap_identity_req = true;
6225 }
6226
6227 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
6228 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
6229 return -EINVAL;
6230 new_triggers.four_way_handshake = true;
6231 }
6232
6233 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
6234 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
6235 return -EINVAL;
6236 new_triggers.rfkill_release = true;
6237 }
6238
Johannes Bergff1b6e62011-05-04 15:37:28 +02006239 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
6240 struct nlattr *pat;
6241 int n_patterns = 0;
6242 int rem, pat_len, mask_len;
6243 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
6244
6245 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6246 rem)
6247 n_patterns++;
6248 if (n_patterns > wowlan->n_patterns)
6249 return -EINVAL;
6250
6251 new_triggers.patterns = kcalloc(n_patterns,
6252 sizeof(new_triggers.patterns[0]),
6253 GFP_KERNEL);
6254 if (!new_triggers.patterns)
6255 return -ENOMEM;
6256
6257 new_triggers.n_patterns = n_patterns;
6258 i = 0;
6259
6260 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
6261 rem) {
6262 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
6263 nla_data(pat), nla_len(pat), NULL);
6264 err = -EINVAL;
6265 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
6266 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
6267 goto error;
6268 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
6269 mask_len = DIV_ROUND_UP(pat_len, 8);
6270 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
6271 mask_len)
6272 goto error;
6273 if (pat_len > wowlan->pattern_max_len ||
6274 pat_len < wowlan->pattern_min_len)
6275 goto error;
6276
6277 new_triggers.patterns[i].mask =
6278 kmalloc(mask_len + pat_len, GFP_KERNEL);
6279 if (!new_triggers.patterns[i].mask) {
6280 err = -ENOMEM;
6281 goto error;
6282 }
6283 new_triggers.patterns[i].pattern =
6284 new_triggers.patterns[i].mask + mask_len;
6285 memcpy(new_triggers.patterns[i].mask,
6286 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
6287 mask_len);
6288 new_triggers.patterns[i].pattern_len = pat_len;
6289 memcpy(new_triggers.patterns[i].pattern,
6290 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
6291 pat_len);
6292 i++;
6293 }
6294 }
6295
6296 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
6297 struct cfg80211_wowlan *ntrig;
6298 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
6299 GFP_KERNEL);
6300 if (!ntrig) {
6301 err = -ENOMEM;
6302 goto error;
6303 }
6304 cfg80211_rdev_free_wowlan(rdev);
6305 rdev->wowlan = ntrig;
6306 } else {
6307 no_triggers:
6308 cfg80211_rdev_free_wowlan(rdev);
6309 rdev->wowlan = NULL;
6310 }
6311
6312 return 0;
6313 error:
6314 for (i = 0; i < new_triggers.n_patterns; i++)
6315 kfree(new_triggers.patterns[i].mask);
6316 kfree(new_triggers.patterns);
6317 return err;
6318}
6319
Johannes Berge5497d72011-07-05 16:35:40 +02006320static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
6321{
6322 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6323 struct net_device *dev = info->user_ptr[1];
6324 struct wireless_dev *wdev = dev->ieee80211_ptr;
6325 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
6326 struct cfg80211_gtk_rekey_data rekey_data;
6327 int err;
6328
6329 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
6330 return -EINVAL;
6331
6332 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
6333 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
6334 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
6335 nl80211_rekey_policy);
6336 if (err)
6337 return err;
6338
6339 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
6340 return -ERANGE;
6341 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
6342 return -ERANGE;
6343 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
6344 return -ERANGE;
6345
6346 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
6347 NL80211_KEK_LEN);
6348 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
6349 NL80211_KCK_LEN);
6350 memcpy(rekey_data.replay_ctr,
6351 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
6352 NL80211_REPLAY_CTR_LEN);
6353
6354 wdev_lock(wdev);
6355 if (!wdev->current_bss) {
6356 err = -ENOTCONN;
6357 goto out;
6358 }
6359
6360 if (!rdev->ops->set_rekey_data) {
6361 err = -EOPNOTSUPP;
6362 goto out;
6363 }
6364
6365 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
6366 out:
6367 wdev_unlock(wdev);
6368 return err;
6369}
6370
Johannes Berg28946da2011-11-04 11:18:12 +01006371static int nl80211_register_unexpected_frame(struct sk_buff *skb,
6372 struct genl_info *info)
6373{
6374 struct net_device *dev = info->user_ptr[1];
6375 struct wireless_dev *wdev = dev->ieee80211_ptr;
6376
6377 if (wdev->iftype != NL80211_IFTYPE_AP &&
6378 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6379 return -EINVAL;
6380
6381 if (wdev->ap_unexpected_nlpid)
6382 return -EBUSY;
6383
6384 wdev->ap_unexpected_nlpid = info->snd_pid;
6385 return 0;
6386}
6387
Johannes Berg7f6cf312011-11-04 11:18:15 +01006388static int nl80211_probe_client(struct sk_buff *skb,
6389 struct genl_info *info)
6390{
6391 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6392 struct net_device *dev = info->user_ptr[1];
6393 struct wireless_dev *wdev = dev->ieee80211_ptr;
6394 struct sk_buff *msg;
6395 void *hdr;
6396 const u8 *addr;
6397 u64 cookie;
6398 int err;
6399
6400 if (wdev->iftype != NL80211_IFTYPE_AP &&
6401 wdev->iftype != NL80211_IFTYPE_P2P_GO)
6402 return -EOPNOTSUPP;
6403
6404 if (!info->attrs[NL80211_ATTR_MAC])
6405 return -EINVAL;
6406
6407 if (!rdev->ops->probe_client)
6408 return -EOPNOTSUPP;
6409
6410 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6411 if (!msg)
6412 return -ENOMEM;
6413
6414 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
6415 NL80211_CMD_PROBE_CLIENT);
6416
6417 if (IS_ERR(hdr)) {
6418 err = PTR_ERR(hdr);
6419 goto free_msg;
6420 }
6421
6422 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
6423
6424 err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
6425 if (err)
6426 goto free_msg;
6427
6428 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
6429
6430 genlmsg_end(msg, hdr);
6431
6432 return genlmsg_reply(msg, info);
6433
6434 nla_put_failure:
6435 err = -ENOBUFS;
6436 free_msg:
6437 nlmsg_free(msg);
6438 return err;
6439}
6440
Johannes Berg5e760232011-11-04 11:18:17 +01006441static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
6442{
6443 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6444
6445 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
6446 return -EOPNOTSUPP;
6447
6448 if (rdev->ap_beacons_nlpid)
6449 return -EBUSY;
6450
6451 rdev->ap_beacons_nlpid = info->snd_pid;
6452
6453 return 0;
6454}
6455
Jouni Malinen2b4303f2013-03-19 14:30:49 +05306456static int nl80211_update_ft_ies(struct sk_buff *skb, struct genl_info *info)
6457{
6458 struct cfg80211_registered_device *rdev = info->user_ptr[0];
6459 struct cfg80211_update_ft_ies_params ft_params;
6460 struct net_device *dev = info->user_ptr[1];
6461
6462 if (!rdev->ops->update_ft_ies)
6463 return -EOPNOTSUPP;
6464
6465 if (!info->attrs[NL80211_ATTR_MDID] ||
6466 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
6467 return -EINVAL;
6468
6469 memset(&ft_params, 0, sizeof(ft_params));
6470 ft_params.md = nla_get_u16(info->attrs[NL80211_ATTR_MDID]);
6471 ft_params.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
6472 ft_params.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
6473
6474 return rdev->ops->update_ft_ies(&rdev->wiphy, dev, &ft_params);
6475}
6476
Johannes Berg4c476992010-10-04 21:36:35 +02006477#define NL80211_FLAG_NEED_WIPHY 0x01
6478#define NL80211_FLAG_NEED_NETDEV 0x02
6479#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02006480#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
6481#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
6482 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02006483
6484static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
6485 struct genl_info *info)
6486{
6487 struct cfg80211_registered_device *rdev;
6488 struct net_device *dev;
6489 int err;
6490 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
6491
6492 if (rtnl)
6493 rtnl_lock();
6494
6495 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
6496 rdev = cfg80211_get_dev_from_info(info);
6497 if (IS_ERR(rdev)) {
6498 if (rtnl)
6499 rtnl_unlock();
6500 return PTR_ERR(rdev);
6501 }
6502 info->user_ptr[0] = rdev;
6503 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
Johannes Berg00918d32011-12-13 17:22:05 +01006504 err = get_rdev_dev_by_ifindex(genl_info_net(info), info->attrs,
6505 &rdev, &dev);
Johannes Berg4c476992010-10-04 21:36:35 +02006506 if (err) {
6507 if (rtnl)
6508 rtnl_unlock();
6509 return err;
6510 }
Johannes Berg41265712010-10-04 21:14:05 +02006511 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
6512 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02006513 cfg80211_unlock_rdev(rdev);
6514 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02006515 if (rtnl)
6516 rtnl_unlock();
6517 return -ENETDOWN;
6518 }
Johannes Berg4c476992010-10-04 21:36:35 +02006519 info->user_ptr[0] = rdev;
6520 info->user_ptr[1] = dev;
6521 }
6522
6523 return 0;
6524}
6525
6526static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
6527 struct genl_info *info)
6528{
6529 if (info->user_ptr[0])
6530 cfg80211_unlock_rdev(info->user_ptr[0]);
6531 if (info->user_ptr[1])
6532 dev_put(info->user_ptr[1]);
6533 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
6534 rtnl_unlock();
6535}
6536
Johannes Berg55682962007-09-20 13:09:35 -04006537static struct genl_ops nl80211_ops[] = {
6538 {
6539 .cmd = NL80211_CMD_GET_WIPHY,
6540 .doit = nl80211_get_wiphy,
6541 .dumpit = nl80211_dump_wiphy,
6542 .policy = nl80211_policy,
6543 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006544 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04006545 },
6546 {
6547 .cmd = NL80211_CMD_SET_WIPHY,
6548 .doit = nl80211_set_wiphy,
6549 .policy = nl80211_policy,
6550 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006551 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006552 },
6553 {
6554 .cmd = NL80211_CMD_GET_INTERFACE,
6555 .doit = nl80211_get_interface,
6556 .dumpit = nl80211_dump_interface,
6557 .policy = nl80211_policy,
6558 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006559 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04006560 },
6561 {
6562 .cmd = NL80211_CMD_SET_INTERFACE,
6563 .doit = nl80211_set_interface,
6564 .policy = nl80211_policy,
6565 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006566 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6567 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006568 },
6569 {
6570 .cmd = NL80211_CMD_NEW_INTERFACE,
6571 .doit = nl80211_new_interface,
6572 .policy = nl80211_policy,
6573 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006574 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6575 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006576 },
6577 {
6578 .cmd = NL80211_CMD_DEL_INTERFACE,
6579 .doit = nl80211_del_interface,
6580 .policy = nl80211_policy,
6581 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006582 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6583 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006584 },
Johannes Berg41ade002007-12-19 02:03:29 +01006585 {
6586 .cmd = NL80211_CMD_GET_KEY,
6587 .doit = nl80211_get_key,
6588 .policy = nl80211_policy,
6589 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006590 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006591 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006592 },
6593 {
6594 .cmd = NL80211_CMD_SET_KEY,
6595 .doit = nl80211_set_key,
6596 .policy = nl80211_policy,
6597 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006598 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006599 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006600 },
6601 {
6602 .cmd = NL80211_CMD_NEW_KEY,
6603 .doit = nl80211_new_key,
6604 .policy = nl80211_policy,
6605 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006606 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006607 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006608 },
6609 {
6610 .cmd = NL80211_CMD_DEL_KEY,
6611 .doit = nl80211_del_key,
6612 .policy = nl80211_policy,
6613 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006614 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006615 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006616 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01006617 {
6618 .cmd = NL80211_CMD_SET_BEACON,
6619 .policy = nl80211_policy,
6620 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006621 .doit = nl80211_set_beacon,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006622 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006623 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006624 },
6625 {
Johannes Berg88600202012-02-13 15:17:18 +01006626 .cmd = NL80211_CMD_START_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006627 .policy = nl80211_policy,
6628 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006629 .doit = nl80211_start_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006630 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006631 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006632 },
6633 {
Johannes Berg88600202012-02-13 15:17:18 +01006634 .cmd = NL80211_CMD_STOP_AP,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006635 .policy = nl80211_policy,
6636 .flags = GENL_ADMIN_PERM,
Johannes Berg88600202012-02-13 15:17:18 +01006637 .doit = nl80211_stop_ap,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006638 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006639 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006640 },
Johannes Berg5727ef12007-12-19 02:03:34 +01006641 {
6642 .cmd = NL80211_CMD_GET_STATION,
6643 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006644 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01006645 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02006646 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6647 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006648 },
6649 {
6650 .cmd = NL80211_CMD_SET_STATION,
6651 .doit = nl80211_set_station,
6652 .policy = nl80211_policy,
6653 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006654 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006655 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006656 },
6657 {
6658 .cmd = NL80211_CMD_NEW_STATION,
6659 .doit = nl80211_new_station,
6660 .policy = nl80211_policy,
6661 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006662 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006663 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006664 },
6665 {
6666 .cmd = NL80211_CMD_DEL_STATION,
6667 .doit = nl80211_del_station,
6668 .policy = nl80211_policy,
6669 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006670 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006671 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006672 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006673 {
6674 .cmd = NL80211_CMD_GET_MPATH,
6675 .doit = nl80211_get_mpath,
6676 .dumpit = nl80211_dump_mpath,
6677 .policy = nl80211_policy,
6678 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006679 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006680 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006681 },
6682 {
6683 .cmd = NL80211_CMD_SET_MPATH,
6684 .doit = nl80211_set_mpath,
6685 .policy = nl80211_policy,
6686 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006687 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006688 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006689 },
6690 {
6691 .cmd = NL80211_CMD_NEW_MPATH,
6692 .doit = nl80211_new_mpath,
6693 .policy = nl80211_policy,
6694 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006695 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006696 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006697 },
6698 {
6699 .cmd = NL80211_CMD_DEL_MPATH,
6700 .doit = nl80211_del_mpath,
6701 .policy = nl80211_policy,
6702 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006703 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006704 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006705 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006706 {
6707 .cmd = NL80211_CMD_SET_BSS,
6708 .doit = nl80211_set_bss,
6709 .policy = nl80211_policy,
6710 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006711 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006712 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006713 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006714 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08006715 .cmd = NL80211_CMD_GET_REG,
6716 .doit = nl80211_get_reg,
6717 .policy = nl80211_policy,
6718 /* can be retrieved by unprivileged users */
6719 },
6720 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006721 .cmd = NL80211_CMD_SET_REG,
6722 .doit = nl80211_set_reg,
6723 .policy = nl80211_policy,
6724 .flags = GENL_ADMIN_PERM,
6725 },
6726 {
6727 .cmd = NL80211_CMD_REQ_SET_REG,
6728 .doit = nl80211_req_set_reg,
6729 .policy = nl80211_policy,
6730 .flags = GENL_ADMIN_PERM,
6731 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006732 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006733 .cmd = NL80211_CMD_GET_MESH_CONFIG,
6734 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006735 .policy = nl80211_policy,
6736 /* can be retrieved by unprivileged users */
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006737 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006738 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006739 },
6740 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006741 .cmd = NL80211_CMD_SET_MESH_CONFIG,
6742 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006743 .policy = nl80211_policy,
6744 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01006745 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006746 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006747 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02006748 {
Johannes Berg2a519312009-02-10 21:25:55 +01006749 .cmd = NL80211_CMD_TRIGGER_SCAN,
6750 .doit = nl80211_trigger_scan,
6751 .policy = nl80211_policy,
6752 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006753 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006754 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01006755 },
6756 {
6757 .cmd = NL80211_CMD_GET_SCAN,
6758 .policy = nl80211_policy,
6759 .dumpit = nl80211_dump_scan,
6760 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02006761 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03006762 .cmd = NL80211_CMD_START_SCHED_SCAN,
6763 .doit = nl80211_start_sched_scan,
6764 .policy = nl80211_policy,
6765 .flags = GENL_ADMIN_PERM,
6766 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6767 NL80211_FLAG_NEED_RTNL,
6768 },
6769 {
6770 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
6771 .doit = nl80211_stop_sched_scan,
6772 .policy = nl80211_policy,
6773 .flags = GENL_ADMIN_PERM,
6774 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6775 NL80211_FLAG_NEED_RTNL,
6776 },
6777 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02006778 .cmd = NL80211_CMD_AUTHENTICATE,
6779 .doit = nl80211_authenticate,
6780 .policy = nl80211_policy,
6781 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006782 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006783 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006784 },
6785 {
6786 .cmd = NL80211_CMD_ASSOCIATE,
6787 .doit = nl80211_associate,
6788 .policy = nl80211_policy,
6789 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006790 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006791 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006792 },
6793 {
6794 .cmd = NL80211_CMD_DEAUTHENTICATE,
6795 .doit = nl80211_deauthenticate,
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,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006800 },
6801 {
6802 .cmd = NL80211_CMD_DISASSOCIATE,
6803 .doit = nl80211_disassociate,
6804 .policy = nl80211_policy,
6805 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006806 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006807 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006808 },
Johannes Berg04a773a2009-04-19 21:24:32 +02006809 {
6810 .cmd = NL80211_CMD_JOIN_IBSS,
6811 .doit = nl80211_join_ibss,
6812 .policy = nl80211_policy,
6813 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006814 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006815 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006816 },
6817 {
6818 .cmd = NL80211_CMD_LEAVE_IBSS,
6819 .doit = nl80211_leave_ibss,
6820 .policy = nl80211_policy,
6821 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006822 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006823 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006824 },
Johannes Bergaff89a92009-07-01 21:26:51 +02006825#ifdef CONFIG_NL80211_TESTMODE
6826 {
6827 .cmd = NL80211_CMD_TESTMODE,
6828 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006829 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02006830 .policy = nl80211_policy,
6831 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006832 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6833 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02006834 },
6835#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02006836 {
6837 .cmd = NL80211_CMD_CONNECT,
6838 .doit = nl80211_connect,
6839 .policy = nl80211_policy,
6840 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006841 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006842 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006843 },
6844 {
6845 .cmd = NL80211_CMD_DISCONNECT,
6846 .doit = nl80211_disconnect,
6847 .policy = nl80211_policy,
6848 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006849 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006850 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006851 },
Johannes Berg463d0182009-07-14 00:33:35 +02006852 {
6853 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
6854 .doit = nl80211_wiphy_netns,
6855 .policy = nl80211_policy,
6856 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006857 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6858 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02006859 },
Holger Schurig61fa7132009-11-11 12:25:40 +01006860 {
6861 .cmd = NL80211_CMD_GET_SURVEY,
6862 .policy = nl80211_policy,
6863 .dumpit = nl80211_dump_survey,
6864 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006865 {
6866 .cmd = NL80211_CMD_SET_PMKSA,
6867 .doit = nl80211_setdel_pmksa,
6868 .policy = nl80211_policy,
6869 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006870 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006871 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006872 },
6873 {
6874 .cmd = NL80211_CMD_DEL_PMKSA,
6875 .doit = nl80211_setdel_pmksa,
6876 .policy = nl80211_policy,
6877 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006878 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006879 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006880 },
6881 {
6882 .cmd = NL80211_CMD_FLUSH_PMKSA,
6883 .doit = nl80211_flush_pmksa,
6884 .policy = nl80211_policy,
6885 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02006886 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006887 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006888 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006889 {
6890 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
6891 .doit = nl80211_remain_on_channel,
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,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006896 },
6897 {
6898 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6899 .doit = nl80211_cancel_remain_on_channel,
6900 .policy = nl80211_policy,
6901 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006902 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006903 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006904 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006905 {
6906 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
6907 .doit = nl80211_set_tx_bitrate_mask,
6908 .policy = nl80211_policy,
6909 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006910 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6911 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006912 },
Jouni Malinen026331c2010-02-15 12:53:10 +02006913 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006914 .cmd = NL80211_CMD_REGISTER_FRAME,
6915 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006916 .policy = nl80211_policy,
6917 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006918 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6919 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006920 },
6921 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006922 .cmd = NL80211_CMD_FRAME,
6923 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006924 .policy = nl80211_policy,
6925 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006926 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006927 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006928 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02006929 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006930 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
6931 .doit = nl80211_tx_mgmt_cancel_wait,
6932 .policy = nl80211_policy,
6933 .flags = GENL_ADMIN_PERM,
6934 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6935 NL80211_FLAG_NEED_RTNL,
6936 },
6937 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02006938 .cmd = NL80211_CMD_SET_POWER_SAVE,
6939 .doit = nl80211_set_power_save,
6940 .policy = nl80211_policy,
6941 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006942 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6943 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006944 },
6945 {
6946 .cmd = NL80211_CMD_GET_POWER_SAVE,
6947 .doit = nl80211_get_power_save,
6948 .policy = nl80211_policy,
6949 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006950 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6951 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006952 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006953 {
6954 .cmd = NL80211_CMD_SET_CQM,
6955 .doit = nl80211_set_cqm,
6956 .policy = nl80211_policy,
6957 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006958 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6959 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006960 },
Johannes Bergf444de02010-05-05 15:25:02 +02006961 {
6962 .cmd = NL80211_CMD_SET_CHANNEL,
6963 .doit = nl80211_set_channel,
6964 .policy = nl80211_policy,
6965 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006966 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6967 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02006968 },
Bill Jordane8347eb2010-10-01 13:54:28 -04006969 {
6970 .cmd = NL80211_CMD_SET_WDS_PEER,
6971 .doit = nl80211_set_wds_peer,
6972 .policy = nl80211_policy,
6973 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02006974 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6975 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04006976 },
Johannes Berg29cbe682010-12-03 09:20:44 +01006977 {
6978 .cmd = NL80211_CMD_JOIN_MESH,
6979 .doit = nl80211_join_mesh,
6980 .policy = nl80211_policy,
6981 .flags = GENL_ADMIN_PERM,
6982 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6983 NL80211_FLAG_NEED_RTNL,
6984 },
6985 {
6986 .cmd = NL80211_CMD_LEAVE_MESH,
6987 .doit = nl80211_leave_mesh,
6988 .policy = nl80211_policy,
6989 .flags = GENL_ADMIN_PERM,
6990 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6991 NL80211_FLAG_NEED_RTNL,
6992 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02006993 {
6994 .cmd = NL80211_CMD_GET_WOWLAN,
6995 .doit = nl80211_get_wowlan,
6996 .policy = nl80211_policy,
6997 /* can be retrieved by unprivileged users */
6998 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6999 NL80211_FLAG_NEED_RTNL,
7000 },
7001 {
7002 .cmd = NL80211_CMD_SET_WOWLAN,
7003 .doit = nl80211_set_wowlan,
7004 .policy = nl80211_policy,
7005 .flags = GENL_ADMIN_PERM,
7006 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7007 NL80211_FLAG_NEED_RTNL,
7008 },
Johannes Berge5497d72011-07-05 16:35:40 +02007009 {
7010 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
7011 .doit = nl80211_set_rekey_data,
7012 .policy = nl80211_policy,
7013 .flags = GENL_ADMIN_PERM,
7014 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7015 NL80211_FLAG_NEED_RTNL,
7016 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03007017 {
7018 .cmd = NL80211_CMD_TDLS_MGMT,
7019 .doit = nl80211_tdls_mgmt,
7020 .policy = nl80211_policy,
7021 .flags = GENL_ADMIN_PERM,
7022 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7023 NL80211_FLAG_NEED_RTNL,
7024 },
7025 {
7026 .cmd = NL80211_CMD_TDLS_OPER,
7027 .doit = nl80211_tdls_oper,
7028 .policy = nl80211_policy,
7029 .flags = GENL_ADMIN_PERM,
7030 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7031 NL80211_FLAG_NEED_RTNL,
7032 },
Johannes Berg28946da2011-11-04 11:18:12 +01007033 {
7034 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
7035 .doit = nl80211_register_unexpected_frame,
7036 .policy = nl80211_policy,
7037 .flags = GENL_ADMIN_PERM,
7038 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7039 NL80211_FLAG_NEED_RTNL,
7040 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01007041 {
7042 .cmd = NL80211_CMD_PROBE_CLIENT,
7043 .doit = nl80211_probe_client,
7044 .policy = nl80211_policy,
7045 .flags = GENL_ADMIN_PERM,
Johannes Berg2b5f8b02012-04-02 10:51:55 +02007046 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg7f6cf312011-11-04 11:18:15 +01007047 NL80211_FLAG_NEED_RTNL,
7048 },
Johannes Berg5e760232011-11-04 11:18:17 +01007049 {
7050 .cmd = NL80211_CMD_REGISTER_BEACONS,
7051 .doit = nl80211_register_beacons,
7052 .policy = nl80211_policy,
7053 .flags = GENL_ADMIN_PERM,
7054 .internal_flags = NL80211_FLAG_NEED_WIPHY |
7055 NL80211_FLAG_NEED_RTNL,
7056 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01007057 {
7058 .cmd = NL80211_CMD_SET_NOACK_MAP,
7059 .doit = nl80211_set_noack_map,
7060 .policy = nl80211_policy,
7061 .flags = GENL_ADMIN_PERM,
7062 .internal_flags = NL80211_FLAG_NEED_NETDEV |
7063 NL80211_FLAG_NEED_RTNL,
7064 },
Jouni Malinen2b4303f2013-03-19 14:30:49 +05307065 {
7066 .cmd = NL80211_CMD_UPDATE_FT_IES,
7067 .doit = nl80211_update_ft_ies,
7068 .policy = nl80211_policy,
7069 .flags = GENL_ADMIN_PERM,
7070 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
7071 NL80211_FLAG_NEED_RTNL,
7072 },
Simon Wunderlich1d9d9212011-11-18 14:20:43 +01007073
Johannes Berg55682962007-09-20 13:09:35 -04007074};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007075
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007076static struct genl_multicast_group nl80211_mlme_mcgrp = {
7077 .name = "mlme",
7078};
Johannes Berg55682962007-09-20 13:09:35 -04007079
7080/* multicast groups */
7081static struct genl_multicast_group nl80211_config_mcgrp = {
7082 .name = "config",
7083};
Johannes Berg2a519312009-02-10 21:25:55 +01007084static struct genl_multicast_group nl80211_scan_mcgrp = {
7085 .name = "scan",
7086};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007087static struct genl_multicast_group nl80211_regulatory_mcgrp = {
7088 .name = "regulatory",
7089};
Johannes Berg55682962007-09-20 13:09:35 -04007090
7091/* notification functions */
7092
7093void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
7094{
7095 struct sk_buff *msg;
7096
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007097 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04007098 if (!msg)
7099 return;
7100
7101 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
7102 nlmsg_free(msg);
7103 return;
7104 }
7105
Johannes Berg463d0182009-07-14 00:33:35 +02007106 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7107 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04007108}
7109
Johannes Berg362a4152009-05-24 16:43:15 +02007110static int nl80211_add_scan_req(struct sk_buff *msg,
7111 struct cfg80211_registered_device *rdev)
7112{
7113 struct cfg80211_scan_request *req = rdev->scan_req;
7114 struct nlattr *nest;
7115 int i;
7116
Johannes Berg667503d2009-07-07 03:56:11 +02007117 ASSERT_RDEV_LOCK(rdev);
7118
Johannes Berg362a4152009-05-24 16:43:15 +02007119 if (WARN_ON(!req))
7120 return 0;
7121
7122 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
7123 if (!nest)
7124 goto nla_put_failure;
7125 for (i = 0; i < req->n_ssids; i++)
7126 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
7127 nla_nest_end(msg, nest);
7128
7129 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
7130 if (!nest)
7131 goto nla_put_failure;
7132 for (i = 0; i < req->n_channels; i++)
7133 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
7134 nla_nest_end(msg, nest);
7135
7136 if (req->ie)
7137 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
7138
7139 return 0;
7140 nla_put_failure:
7141 return -ENOBUFS;
7142}
7143
Johannes Berga538e2d2009-06-16 19:56:42 +02007144static int nl80211_send_scan_msg(struct sk_buff *msg,
7145 struct cfg80211_registered_device *rdev,
7146 struct net_device *netdev,
7147 u32 pid, u32 seq, int flags,
7148 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01007149{
7150 void *hdr;
7151
7152 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7153 if (!hdr)
7154 return -1;
7155
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05007156 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01007157 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7158
Johannes Berg362a4152009-05-24 16:43:15 +02007159 /* ignore errors and send incomplete event anyway */
7160 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01007161
7162 return genlmsg_end(msg, hdr);
7163
7164 nla_put_failure:
7165 genlmsg_cancel(msg, hdr);
7166 return -EMSGSIZE;
7167}
7168
Luciano Coelho807f8a82011-05-11 17:09:35 +03007169static int
7170nl80211_send_sched_scan_msg(struct sk_buff *msg,
7171 struct cfg80211_registered_device *rdev,
7172 struct net_device *netdev,
7173 u32 pid, u32 seq, int flags, u32 cmd)
7174{
7175 void *hdr;
7176
7177 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
7178 if (!hdr)
7179 return -1;
7180
7181 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7182 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7183
7184 return genlmsg_end(msg, hdr);
7185
7186 nla_put_failure:
7187 genlmsg_cancel(msg, hdr);
7188 return -EMSGSIZE;
7189}
7190
Johannes Berga538e2d2009-06-16 19:56:42 +02007191void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
7192 struct net_device *netdev)
7193{
7194 struct sk_buff *msg;
7195
7196 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7197 if (!msg)
7198 return;
7199
7200 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7201 NL80211_CMD_TRIGGER_SCAN) < 0) {
7202 nlmsg_free(msg);
7203 return;
7204 }
7205
Johannes Berg463d0182009-07-14 00:33:35 +02007206 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7207 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02007208}
7209
Johannes Berg2a519312009-02-10 21:25:55 +01007210void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
7211 struct net_device *netdev)
7212{
7213 struct sk_buff *msg;
7214
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007215 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007216 if (!msg)
7217 return;
7218
Johannes Berga538e2d2009-06-16 19:56:42 +02007219 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7220 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007221 nlmsg_free(msg);
7222 return;
7223 }
7224
Johannes Berg463d0182009-07-14 00:33:35 +02007225 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7226 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007227}
7228
7229void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
7230 struct net_device *netdev)
7231{
7232 struct sk_buff *msg;
7233
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007234 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007235 if (!msg)
7236 return;
7237
Johannes Berga538e2d2009-06-16 19:56:42 +02007238 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
7239 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01007240 nlmsg_free(msg);
7241 return;
7242 }
7243
Johannes Berg463d0182009-07-14 00:33:35 +02007244 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7245 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01007246}
7247
Luciano Coelho807f8a82011-05-11 17:09:35 +03007248void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
7249 struct net_device *netdev)
7250{
7251 struct sk_buff *msg;
7252
7253 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
7254 if (!msg)
7255 return;
7256
7257 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
7258 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
7259 nlmsg_free(msg);
7260 return;
7261 }
7262
7263 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7264 nl80211_scan_mcgrp.id, GFP_KERNEL);
7265}
7266
7267void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
7268 struct net_device *netdev, u32 cmd)
7269{
7270 struct sk_buff *msg;
7271
7272 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
7273 if (!msg)
7274 return;
7275
7276 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
7277 nlmsg_free(msg);
7278 return;
7279 }
7280
7281 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7282 nl80211_scan_mcgrp.id, GFP_KERNEL);
7283}
7284
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007285/*
7286 * This can happen on global regulatory changes or device specific settings
7287 * based on custom world regulatory domains.
7288 */
7289void nl80211_send_reg_change_event(struct regulatory_request *request)
7290{
7291 struct sk_buff *msg;
7292 void *hdr;
7293
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007294 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007295 if (!msg)
7296 return;
7297
7298 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
7299 if (!hdr) {
7300 nlmsg_free(msg);
7301 return;
7302 }
7303
7304 /* Userspace can always count this one always being set */
7305 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
7306
7307 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
7308 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7309 NL80211_REGDOM_TYPE_WORLD);
7310 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
7311 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7312 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
7313 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
7314 request->intersect)
7315 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7316 NL80211_REGDOM_TYPE_INTERSECTION);
7317 else {
7318 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
7319 NL80211_REGDOM_TYPE_COUNTRY);
7320 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
7321 }
7322
7323 if (wiphy_idx_valid(request->wiphy_idx))
7324 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
7325
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007326 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007327
Johannes Bergbc43b282009-07-25 10:54:13 +02007328 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02007329 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02007330 GFP_ATOMIC);
7331 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007332
7333 return;
7334
7335nla_put_failure:
7336 genlmsg_cancel(msg, hdr);
7337 nlmsg_free(msg);
7338}
7339
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007340static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
7341 struct net_device *netdev,
7342 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007343 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007344{
7345 struct sk_buff *msg;
7346 void *hdr;
7347
Johannes Berge6d6e342009-07-01 21:26:47 +02007348 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007349 if (!msg)
7350 return;
7351
7352 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7353 if (!hdr) {
7354 nlmsg_free(msg);
7355 return;
7356 }
7357
7358 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7359 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7360 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7361
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007362 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007363
Johannes Berg463d0182009-07-14 00:33:35 +02007364 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7365 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007366 return;
7367
7368 nla_put_failure:
7369 genlmsg_cancel(msg, hdr);
7370 nlmsg_free(msg);
7371}
7372
7373void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007374 struct net_device *netdev, const u8 *buf,
7375 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007376{
7377 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007378 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007379}
7380
7381void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
7382 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007383 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007384{
Johannes Berge6d6e342009-07-01 21:26:47 +02007385 nl80211_send_mlme_event(rdev, netdev, buf, len,
7386 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007387}
7388
Jouni Malinen53b46b82009-03-27 20:53:56 +02007389void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007390 struct net_device *netdev, const u8 *buf,
7391 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007392{
7393 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007394 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007395}
7396
Jouni Malinen53b46b82009-03-27 20:53:56 +02007397void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
7398 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02007399 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007400{
7401 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02007402 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007403}
7404
Jouni Malinencf4e5942010-12-16 00:52:40 +02007405void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
7406 struct net_device *netdev, const u8 *buf,
7407 size_t len, gfp_t gfp)
7408{
7409 nl80211_send_mlme_event(rdev, netdev, buf, len,
7410 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
7411}
7412
7413void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
7414 struct net_device *netdev, const u8 *buf,
7415 size_t len, gfp_t gfp)
7416{
7417 nl80211_send_mlme_event(rdev, netdev, buf, len,
7418 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
7419}
7420
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04007421static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
7422 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02007423 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007424{
7425 struct sk_buff *msg;
7426 void *hdr;
7427
Johannes Berge6d6e342009-07-01 21:26:47 +02007428 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007429 if (!msg)
7430 return;
7431
7432 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7433 if (!hdr) {
7434 nlmsg_free(msg);
7435 return;
7436 }
7437
7438 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7439 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7440 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
7441 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7442
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007443 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03007444
Johannes Berg463d0182009-07-14 00:33:35 +02007445 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7446 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007447 return;
7448
7449 nla_put_failure:
7450 genlmsg_cancel(msg, hdr);
7451 nlmsg_free(msg);
7452}
7453
7454void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007455 struct net_device *netdev, const u8 *addr,
7456 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007457{
7458 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02007459 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007460}
7461
7462void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02007463 struct net_device *netdev, const u8 *addr,
7464 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03007465{
Johannes Berge6d6e342009-07-01 21:26:47 +02007466 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
7467 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03007468}
7469
Samuel Ortizb23aa672009-07-01 21:26:54 +02007470void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
7471 struct net_device *netdev, const u8 *bssid,
7472 const u8 *req_ie, size_t req_ie_len,
7473 const u8 *resp_ie, size_t resp_ie_len,
7474 u16 status, gfp_t gfp)
7475{
7476 struct sk_buff *msg;
7477 void *hdr;
7478
7479 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7480 if (!msg)
7481 return;
7482
7483 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
7484 if (!hdr) {
7485 nlmsg_free(msg);
7486 return;
7487 }
7488
7489 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7490 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7491 if (bssid)
7492 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7493 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
7494 if (req_ie)
7495 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
7496 if (resp_ie)
7497 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
7498
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007499 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007500
Johannes Berg463d0182009-07-14 00:33:35 +02007501 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7502 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007503 return;
7504
7505 nla_put_failure:
7506 genlmsg_cancel(msg, hdr);
7507 nlmsg_free(msg);
7508
7509}
7510
7511void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
7512 struct net_device *netdev, const u8 *bssid,
7513 const u8 *req_ie, size_t req_ie_len,
7514 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
7515{
7516 struct sk_buff *msg;
7517 void *hdr;
7518
7519 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7520 if (!msg)
7521 return;
7522
7523 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
7524 if (!hdr) {
7525 nlmsg_free(msg);
7526 return;
7527 }
7528
7529 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7530 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7531 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7532 if (req_ie)
7533 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
7534 if (resp_ie)
7535 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
7536
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007537 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007538
Johannes Berg463d0182009-07-14 00:33:35 +02007539 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7540 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007541 return;
7542
7543 nla_put_failure:
7544 genlmsg_cancel(msg, hdr);
7545 nlmsg_free(msg);
7546
7547}
7548
7549void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
7550 struct net_device *netdev, u16 reason,
Johannes Berg667503d2009-07-07 03:56:11 +02007551 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02007552{
7553 struct sk_buff *msg;
7554 void *hdr;
7555
Johannes Berg667503d2009-07-07 03:56:11 +02007556 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007557 if (!msg)
7558 return;
7559
7560 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
7561 if (!hdr) {
7562 nlmsg_free(msg);
7563 return;
7564 }
7565
7566 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7567 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7568 if (from_ap && reason)
7569 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
7570 if (from_ap)
7571 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
7572 if (ie)
7573 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
7574
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007575 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007576
Johannes Berg463d0182009-07-14 00:33:35 +02007577 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7578 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007579 return;
7580
7581 nla_put_failure:
7582 genlmsg_cancel(msg, hdr);
7583 nlmsg_free(msg);
7584
7585}
7586
Johannes Berg04a773a2009-04-19 21:24:32 +02007587void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
7588 struct net_device *netdev, const u8 *bssid,
7589 gfp_t gfp)
7590{
7591 struct sk_buff *msg;
7592 void *hdr;
7593
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007594 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007595 if (!msg)
7596 return;
7597
7598 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
7599 if (!hdr) {
7600 nlmsg_free(msg);
7601 return;
7602 }
7603
7604 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7605 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7606 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7607
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007608 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02007609
Johannes Berg463d0182009-07-14 00:33:35 +02007610 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7611 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007612 return;
7613
7614 nla_put_failure:
7615 genlmsg_cancel(msg, hdr);
7616 nlmsg_free(msg);
7617}
7618
Javier Cardonac93b5e72011-04-07 15:08:34 -07007619void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
7620 struct net_device *netdev,
7621 const u8 *macaddr, const u8* ie, u8 ie_len,
7622 gfp_t gfp)
7623{
7624 struct sk_buff *msg;
7625 void *hdr;
7626
7627 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7628 if (!msg)
7629 return;
7630
7631 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
7632 if (!hdr) {
7633 nlmsg_free(msg);
7634 return;
7635 }
7636
7637 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7638 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7639 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
7640 if (ie_len && ie)
7641 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
7642
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007643 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07007644
7645 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7646 nl80211_mlme_mcgrp.id, gfp);
7647 return;
7648
7649 nla_put_failure:
7650 genlmsg_cancel(msg, hdr);
7651 nlmsg_free(msg);
7652}
7653
Jouni Malinena3b8b052009-03-27 21:59:49 +02007654void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
7655 struct net_device *netdev, const u8 *addr,
7656 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02007657 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02007658{
7659 struct sk_buff *msg;
7660 void *hdr;
7661
Johannes Berge6d6e342009-07-01 21:26:47 +02007662 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007663 if (!msg)
7664 return;
7665
7666 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
7667 if (!hdr) {
7668 nlmsg_free(msg);
7669 return;
7670 }
7671
7672 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7673 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7674 if (addr)
7675 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7676 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03007677 if (key_id != -1)
7678 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007679 if (tsc)
7680 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
7681
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007682 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007683
Johannes Berg463d0182009-07-14 00:33:35 +02007684 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7685 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007686 return;
7687
7688 nla_put_failure:
7689 genlmsg_cancel(msg, hdr);
7690 nlmsg_free(msg);
7691}
7692
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007693void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
7694 struct ieee80211_channel *channel_before,
7695 struct ieee80211_channel *channel_after)
7696{
7697 struct sk_buff *msg;
7698 void *hdr;
7699 struct nlattr *nl_freq;
7700
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007701 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007702 if (!msg)
7703 return;
7704
7705 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
7706 if (!hdr) {
7707 nlmsg_free(msg);
7708 return;
7709 }
7710
7711 /*
7712 * Since we are applying the beacon hint to a wiphy we know its
7713 * wiphy_idx is valid
7714 */
7715 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
7716
7717 /* Before */
7718 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
7719 if (!nl_freq)
7720 goto nla_put_failure;
7721 if (nl80211_msg_put_channel(msg, channel_before))
7722 goto nla_put_failure;
7723 nla_nest_end(msg, nl_freq);
7724
7725 /* After */
7726 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
7727 if (!nl_freq)
7728 goto nla_put_failure;
7729 if (nl80211_msg_put_channel(msg, channel_after))
7730 goto nla_put_failure;
7731 nla_nest_end(msg, nl_freq);
7732
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007733 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007734
Johannes Berg463d0182009-07-14 00:33:35 +02007735 rcu_read_lock();
7736 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
7737 GFP_ATOMIC);
7738 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007739
7740 return;
7741
7742nla_put_failure:
7743 genlmsg_cancel(msg, hdr);
7744 nlmsg_free(msg);
7745}
7746
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007747static void nl80211_send_remain_on_chan_event(
7748 int cmd, struct cfg80211_registered_device *rdev,
7749 struct net_device *netdev, u64 cookie,
7750 struct ieee80211_channel *chan,
7751 enum nl80211_channel_type channel_type,
7752 unsigned int duration, gfp_t gfp)
7753{
7754 struct sk_buff *msg;
7755 void *hdr;
7756
7757 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7758 if (!msg)
7759 return;
7760
7761 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7762 if (!hdr) {
7763 nlmsg_free(msg);
7764 return;
7765 }
7766
7767 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7768 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7769 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
7770 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
7771 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7772
7773 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
7774 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
7775
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007776 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007777
7778 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7779 nl80211_mlme_mcgrp.id, gfp);
7780 return;
7781
7782 nla_put_failure:
7783 genlmsg_cancel(msg, hdr);
7784 nlmsg_free(msg);
7785}
7786
7787void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
7788 struct net_device *netdev, u64 cookie,
7789 struct ieee80211_channel *chan,
7790 enum nl80211_channel_type channel_type,
7791 unsigned int duration, gfp_t gfp)
7792{
7793 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
7794 rdev, netdev, cookie, chan,
7795 channel_type, duration, gfp);
7796}
7797
7798void nl80211_send_remain_on_channel_cancel(
7799 struct cfg80211_registered_device *rdev, struct net_device *netdev,
7800 u64 cookie, struct ieee80211_channel *chan,
7801 enum nl80211_channel_type channel_type, gfp_t gfp)
7802{
7803 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7804 rdev, netdev, cookie, chan,
7805 channel_type, 0, gfp);
7806}
7807
Johannes Berg98b62182009-12-23 13:15:44 +01007808void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
7809 struct net_device *dev, const u8 *mac_addr,
7810 struct station_info *sinfo, gfp_t gfp)
7811{
7812 struct sk_buff *msg;
7813
7814 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7815 if (!msg)
7816 return;
7817
John W. Linville66266b32012-03-15 13:25:41 -04007818 if (nl80211_send_station(msg, 0, 0, 0,
7819 rdev, dev, mac_addr, sinfo) < 0) {
Johannes Berg98b62182009-12-23 13:15:44 +01007820 nlmsg_free(msg);
7821 return;
7822 }
7823
7824 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7825 nl80211_mlme_mcgrp.id, gfp);
7826}
7827
Jouni Malinenec15e682011-03-23 15:29:52 +02007828void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
7829 struct net_device *dev, const u8 *mac_addr,
7830 gfp_t gfp)
7831{
7832 struct sk_buff *msg;
7833 void *hdr;
7834
7835 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7836 if (!msg)
7837 return;
7838
7839 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
7840 if (!hdr) {
7841 nlmsg_free(msg);
7842 return;
7843 }
7844
7845 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7846 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
7847
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007848 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02007849
7850 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7851 nl80211_mlme_mcgrp.id, gfp);
7852 return;
7853
7854 nla_put_failure:
7855 genlmsg_cancel(msg, hdr);
7856 nlmsg_free(msg);
7857}
7858
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007859static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
7860 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01007861{
7862 struct wireless_dev *wdev = dev->ieee80211_ptr;
7863 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7864 struct sk_buff *msg;
7865 void *hdr;
7866 int err;
7867 u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
7868
7869 if (!nlpid)
7870 return false;
7871
7872 msg = nlmsg_new(100, gfp);
7873 if (!msg)
7874 return true;
7875
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007876 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01007877 if (!hdr) {
7878 nlmsg_free(msg);
7879 return true;
7880 }
7881
7882 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7883 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7884 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7885
7886 err = genlmsg_end(msg, hdr);
7887 if (err < 0) {
7888 nlmsg_free(msg);
7889 return true;
7890 }
7891
7892 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7893 return true;
7894
7895 nla_put_failure:
7896 genlmsg_cancel(msg, hdr);
7897 nlmsg_free(msg);
7898 return true;
7899}
7900
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007901bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
7902{
7903 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
7904 addr, gfp);
7905}
7906
7907bool nl80211_unexpected_4addr_frame(struct net_device *dev,
7908 const u8 *addr, gfp_t gfp)
7909{
7910 return __nl80211_unexpected_frame(dev,
7911 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
7912 addr, gfp);
7913}
7914
Johannes Berg2e161f72010-08-12 15:38:38 +02007915int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
7916 struct net_device *netdev, u32 nlpid,
Johannes Berg804483e2012-03-05 22:18:41 +01007917 int freq, int sig_dbm,
7918 const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007919{
7920 struct sk_buff *msg;
7921 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02007922
7923 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7924 if (!msg)
7925 return -ENOMEM;
7926
Johannes Berg2e161f72010-08-12 15:38:38 +02007927 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02007928 if (!hdr) {
7929 nlmsg_free(msg);
7930 return -ENOMEM;
7931 }
7932
7933 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7934 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7935 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
Johannes Berg804483e2012-03-05 22:18:41 +01007936 if (sig_dbm)
7937 NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm);
Jouni Malinen026331c2010-02-15 12:53:10 +02007938 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7939
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007940 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007941
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007942 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
Jouni Malinen026331c2010-02-15 12:53:10 +02007943
7944 nla_put_failure:
7945 genlmsg_cancel(msg, hdr);
7946 nlmsg_free(msg);
7947 return -ENOBUFS;
7948}
7949
Johannes Berg2e161f72010-08-12 15:38:38 +02007950void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
7951 struct net_device *netdev, u64 cookie,
7952 const u8 *buf, size_t len, bool ack,
7953 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007954{
7955 struct sk_buff *msg;
7956 void *hdr;
7957
7958 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7959 if (!msg)
7960 return;
7961
Johannes Berg2e161f72010-08-12 15:38:38 +02007962 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02007963 if (!hdr) {
7964 nlmsg_free(msg);
7965 return;
7966 }
7967
7968 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7969 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7970 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7971 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7972 if (ack)
7973 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7974
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007975 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007976
7977 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
7978 return;
7979
7980 nla_put_failure:
7981 genlmsg_cancel(msg, hdr);
7982 nlmsg_free(msg);
7983}
7984
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007985void
7986nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
7987 struct net_device *netdev,
7988 enum nl80211_cqm_rssi_threshold_event rssi_event,
7989 gfp_t gfp)
7990{
7991 struct sk_buff *msg;
7992 struct nlattr *pinfoattr;
7993 void *hdr;
7994
7995 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7996 if (!msg)
7997 return;
7998
7999 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
8000 if (!hdr) {
8001 nlmsg_free(msg);
8002 return;
8003 }
8004
8005 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8006 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
8007
8008 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
8009 if (!pinfoattr)
8010 goto nla_put_failure;
8011
8012 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
8013 rssi_event);
8014
8015 nla_nest_end(msg, pinfoattr);
8016
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008017 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02008018
8019 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8020 nl80211_mlme_mcgrp.id, gfp);
8021 return;
8022
8023 nla_put_failure:
8024 genlmsg_cancel(msg, hdr);
8025 nlmsg_free(msg);
8026}
8027
Johannes Berge5497d72011-07-05 16:35:40 +02008028void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
8029 struct net_device *netdev, const u8 *bssid,
8030 const u8 *replay_ctr, gfp_t gfp)
8031{
8032 struct sk_buff *msg;
8033 struct nlattr *rekey_attr;
8034 void *hdr;
8035
8036 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8037 if (!msg)
8038 return;
8039
8040 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
8041 if (!hdr) {
8042 nlmsg_free(msg);
8043 return;
8044 }
8045
8046 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8047 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
8048 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
8049
8050 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
8051 if (!rekey_attr)
8052 goto nla_put_failure;
8053
8054 NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
8055 NL80211_REPLAY_CTR_LEN, replay_ctr);
8056
8057 nla_nest_end(msg, rekey_attr);
8058
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008059 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02008060
8061 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8062 nl80211_mlme_mcgrp.id, gfp);
8063 return;
8064
8065 nla_put_failure:
8066 genlmsg_cancel(msg, hdr);
8067 nlmsg_free(msg);
8068}
8069
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008070void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
8071 struct net_device *netdev, int index,
8072 const u8 *bssid, bool preauth, gfp_t gfp)
8073{
8074 struct sk_buff *msg;
8075 struct nlattr *attr;
8076 void *hdr;
8077
8078 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8079 if (!msg)
8080 return;
8081
8082 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
8083 if (!hdr) {
8084 nlmsg_free(msg);
8085 return;
8086 }
8087
8088 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8089 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
8090
8091 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
8092 if (!attr)
8093 goto nla_put_failure;
8094
8095 NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index);
8096 NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid);
8097 if (preauth)
8098 NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH);
8099
8100 nla_nest_end(msg, attr);
8101
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008102 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03008103
8104 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8105 nl80211_mlme_mcgrp.id, gfp);
8106 return;
8107
8108 nla_put_failure:
8109 genlmsg_cancel(msg, hdr);
8110 nlmsg_free(msg);
8111}
8112
Johannes Bergc063dbf2010-11-24 08:10:05 +01008113void
8114nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
8115 struct net_device *netdev, const u8 *peer,
8116 u32 num_packets, gfp_t gfp)
8117{
8118 struct sk_buff *msg;
8119 struct nlattr *pinfoattr;
8120 void *hdr;
8121
8122 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8123 if (!msg)
8124 return;
8125
8126 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
8127 if (!hdr) {
8128 nlmsg_free(msg);
8129 return;
8130 }
8131
8132 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8133 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
8134 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
8135
8136 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
8137 if (!pinfoattr)
8138 goto nla_put_failure;
8139
8140 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
8141
8142 nla_nest_end(msg, pinfoattr);
8143
Johannes Berg3b7b72e2011-10-22 19:05:51 +02008144 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01008145
8146 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8147 nl80211_mlme_mcgrp.id, gfp);
8148 return;
8149
8150 nla_put_failure:
8151 genlmsg_cancel(msg, hdr);
8152 nlmsg_free(msg);
8153}
8154
Johannes Berg7f6cf312011-11-04 11:18:15 +01008155void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
8156 u64 cookie, bool acked, gfp_t gfp)
8157{
8158 struct wireless_dev *wdev = dev->ieee80211_ptr;
8159 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
8160 struct sk_buff *msg;
8161 void *hdr;
8162 int err;
8163
8164 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
8165 if (!msg)
8166 return;
8167
8168 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
8169 if (!hdr) {
8170 nlmsg_free(msg);
8171 return;
8172 }
8173
8174 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8175 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
8176 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
8177 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
8178 if (acked)
8179 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
8180
8181 err = genlmsg_end(msg, hdr);
8182 if (err < 0) {
8183 nlmsg_free(msg);
8184 return;
8185 }
8186
8187 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8188 nl80211_mlme_mcgrp.id, gfp);
8189 return;
8190
8191 nla_put_failure:
8192 genlmsg_cancel(msg, hdr);
8193 nlmsg_free(msg);
8194}
8195EXPORT_SYMBOL(cfg80211_probe_status);
8196
Johannes Berg5e760232011-11-04 11:18:17 +01008197void cfg80211_report_obss_beacon(struct wiphy *wiphy,
8198 const u8 *frame, size_t len,
Johannes Berg804483e2012-03-05 22:18:41 +01008199 int freq, int sig_dbm, gfp_t gfp)
Johannes Berg5e760232011-11-04 11:18:17 +01008200{
8201 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
8202 struct sk_buff *msg;
8203 void *hdr;
8204 u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
8205
8206 if (!nlpid)
8207 return;
8208
8209 msg = nlmsg_new(len + 100, gfp);
8210 if (!msg)
8211 return;
8212
8213 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
8214 if (!hdr) {
8215 nlmsg_free(msg);
8216 return;
8217 }
8218
8219 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8220 if (freq)
8221 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
Johannes Berg804483e2012-03-05 22:18:41 +01008222 if (sig_dbm)
8223 NLA_PUT_U32(msg, NL80211_ATTR_RX_SIGNAL_DBM, sig_dbm);
Johannes Berg5e760232011-11-04 11:18:17 +01008224 NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
8225
8226 genlmsg_end(msg, hdr);
8227
8228 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
8229 return;
8230
8231 nla_put_failure:
8232 genlmsg_cancel(msg, hdr);
8233 nlmsg_free(msg);
8234}
8235EXPORT_SYMBOL(cfg80211_report_obss_beacon);
8236
Jouni Malinen4e1c8442013-01-28 21:11:03 -08008237void cfg80211_tdls_oper_request(struct net_device *dev, const u8 *peer,
8238 enum nl80211_tdls_operation oper,
8239 u16 reason_code, gfp_t gfp)
8240{
8241 struct wireless_dev *wdev = dev->ieee80211_ptr;
8242 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
8243 struct sk_buff *msg;
8244 void *hdr;
8245 int err;
8246
8247 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
8248 if (!msg)
8249 return;
8250
8251 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_TDLS_OPER);
8252 if (!hdr) {
8253 nlmsg_free(msg);
8254 return;
8255 }
8256
8257 if (nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx) ||
8258 nla_put_u32(msg, NL80211_ATTR_IFINDEX, dev->ifindex) ||
8259 nla_put_u8(msg, NL80211_ATTR_TDLS_OPERATION, oper) ||
8260 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, peer) ||
8261 (reason_code > 0 &&
8262 nla_put_u16(msg, NL80211_ATTR_REASON_CODE, reason_code)))
8263 goto nla_put_failure;
8264
8265 err = genlmsg_end(msg, hdr);
8266 if (err < 0) {
8267 nlmsg_free(msg);
8268 return;
8269 }
8270
8271 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8272 nl80211_mlme_mcgrp.id, gfp);
8273 return;
8274
8275 nla_put_failure:
8276 genlmsg_cancel(msg, hdr);
8277 nlmsg_free(msg);
8278}
8279EXPORT_SYMBOL(cfg80211_tdls_oper_request);
8280
Jouni Malinen026331c2010-02-15 12:53:10 +02008281static int nl80211_netlink_notify(struct notifier_block * nb,
8282 unsigned long state,
8283 void *_notify)
8284{
8285 struct netlink_notify *notify = _notify;
8286 struct cfg80211_registered_device *rdev;
8287 struct wireless_dev *wdev;
8288
8289 if (state != NETLINK_URELEASE)
8290 return NOTIFY_DONE;
8291
8292 rcu_read_lock();
8293
Johannes Berg5e760232011-11-04 11:18:17 +01008294 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +02008295 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02008296 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Johannes Berg5e760232011-11-04 11:18:17 +01008297 if (rdev->ap_beacons_nlpid == notify->pid)
8298 rdev->ap_beacons_nlpid = 0;
8299 }
Jouni Malinen026331c2010-02-15 12:53:10 +02008300
8301 rcu_read_unlock();
8302
8303 return NOTIFY_DONE;
8304}
8305
8306static struct notifier_block nl80211_netlink_notifier = {
8307 .notifier_call = nl80211_netlink_notify,
8308};
8309
Jouni Malinen2b4303f2013-03-19 14:30:49 +05308310void cfg80211_ft_event(struct net_device *netdev,
8311 struct cfg80211_ft_event_params *ft_event)
8312{
8313 struct wiphy *wiphy = netdev->ieee80211_ptr->wiphy;
8314 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
8315 struct sk_buff *msg;
8316 void *hdr;
8317 int err;
8318
8319 if (!ft_event->target_ap)
8320 return;
8321
8322 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
8323 if (!msg)
8324 return;
8325
8326 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FT_EVENT);
8327 if (!hdr) {
8328 nlmsg_free(msg);
8329 return;
8330 }
8331
8332 nla_put_u32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
8333 nla_put_u32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
8334 nla_put(msg, NL80211_ATTR_MAC, ETH_ALEN, ft_event->target_ap);
8335 if (ft_event->ies)
8336 nla_put(msg, NL80211_ATTR_IE, ft_event->ies_len, ft_event->ies);
8337 if (ft_event->ric_ies)
8338 nla_put(msg, NL80211_ATTR_IE_RIC, ft_event->ric_ies_len,
8339 ft_event->ric_ies);
8340
8341 err = genlmsg_end(msg, hdr);
8342 if (err < 0) {
8343 nlmsg_free(msg);
8344 return;
8345 }
8346
8347 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
8348 nl80211_mlme_mcgrp.id, GFP_KERNEL);
8349}
8350EXPORT_SYMBOL(cfg80211_ft_event);
8351
Johannes Berg55682962007-09-20 13:09:35 -04008352/* initialisation/exit functions */
8353
8354int nl80211_init(void)
8355{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008356 int err;
Johannes Berg55682962007-09-20 13:09:35 -04008357
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00008358 err = genl_register_family_with_ops(&nl80211_fam,
8359 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04008360 if (err)
8361 return err;
8362
Johannes Berg55682962007-09-20 13:09:35 -04008363 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
8364 if (err)
8365 goto err_out;
8366
Johannes Berg2a519312009-02-10 21:25:55 +01008367 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
8368 if (err)
8369 goto err_out;
8370
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04008371 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
8372 if (err)
8373 goto err_out;
8374
Jouni Malinen6039f6d2009-03-19 13:39:21 +02008375 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
8376 if (err)
8377 goto err_out;
8378
Johannes Bergaff89a92009-07-01 21:26:51 +02008379#ifdef CONFIG_NL80211_TESTMODE
8380 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
8381 if (err)
8382 goto err_out;
8383#endif
8384
Jouni Malinen026331c2010-02-15 12:53:10 +02008385 err = netlink_register_notifier(&nl80211_netlink_notifier);
8386 if (err)
8387 goto err_out;
8388
Johannes Berg55682962007-09-20 13:09:35 -04008389 return 0;
8390 err_out:
8391 genl_unregister_family(&nl80211_fam);
8392 return err;
8393}
8394
8395void nl80211_exit(void)
8396{
Jouni Malinen026331c2010-02-15 12:53:10 +02008397 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04008398 genl_unregister_family(&nl80211_fam);
8399}