blob: 0ef09415c89afdfd5120dfa7f9c0a80b4dfe7a15 [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 Berg463d0182009-07-14 00:33:35 +020050static int get_rdev_dev_by_info_ifindex(struct genl_info *info,
Johannes Berg79c97e92009-07-07 03:56:12 +020051 struct cfg80211_registered_device **rdev,
Johannes Berg55682962007-09-20 13:09:35 -040052 struct net_device **dev)
53{
Johannes Berg463d0182009-07-14 00:33:35 +020054 struct nlattr **attrs = info->attrs;
Johannes Berg55682962007-09-20 13:09:35 -040055 int ifindex;
56
Johannes Bergbba95fe2008-07-29 13:22:51 +020057 if (!attrs[NL80211_ATTR_IFINDEX])
Johannes Berg55682962007-09-20 13:09:35 -040058 return -EINVAL;
59
Johannes Bergbba95fe2008-07-29 13:22:51 +020060 ifindex = nla_get_u32(attrs[NL80211_ATTR_IFINDEX]);
Johannes Berg463d0182009-07-14 00:33:35 +020061 *dev = dev_get_by_index(genl_info_net(info), ifindex);
Johannes Berg55682962007-09-20 13:09:35 -040062 if (!*dev)
63 return -ENODEV;
64
Johannes Berg463d0182009-07-14 00:33:35 +020065 *rdev = cfg80211_get_dev_from_ifindex(genl_info_net(info), ifindex);
Johannes Berg79c97e92009-07-07 03:56:12 +020066 if (IS_ERR(*rdev)) {
Johannes Berg55682962007-09-20 13:09:35 -040067 dev_put(*dev);
Johannes Berg79c97e92009-07-07 03:56:12 +020068 return PTR_ERR(*rdev);
Johannes Berg55682962007-09-20 13:09:35 -040069 }
70
71 return 0;
72}
73
74/* policy for the attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +000075static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = {
Johannes Berg55682962007-09-20 13:09:35 -040076 [NL80211_ATTR_WIPHY] = { .type = NLA_U32 },
77 [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING,
David S. Miller079e24e2009-05-26 21:15:00 -070078 .len = 20-1 },
Jouni Malinen31888482008-10-30 16:59:24 +020079 [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED },
Jouni Malinen72bdcf32008-11-26 16:15:24 +020080 [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 },
Sujith094d05d2008-12-12 11:57:43 +053081 [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 },
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +020082 [NL80211_ATTR_WIPHY_RETRY_SHORT] = { .type = NLA_U8 },
83 [NL80211_ATTR_WIPHY_RETRY_LONG] = { .type = NLA_U8 },
84 [NL80211_ATTR_WIPHY_FRAG_THRESHOLD] = { .type = NLA_U32 },
85 [NL80211_ATTR_WIPHY_RTS_THRESHOLD] = { .type = NLA_U32 },
Lukáš Turek81077e82009-12-21 22:50:47 +010086 [NL80211_ATTR_WIPHY_COVERAGE_CLASS] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -040087
88 [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 },
89 [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 },
90 [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 },
Johannes Berg41ade002007-12-19 02:03:29 +010091
92 [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg3e5d7642009-07-07 14:37:26 +020093 [NL80211_ATTR_PREV_BSSID] = { .type = NLA_BINARY, .len = ETH_ALEN },
Johannes Berg41ade002007-12-19 02:03:29 +010094
Johannes Bergb9454e82009-07-08 13:29:08 +020095 [NL80211_ATTR_KEY] = { .type = NLA_NESTED, },
Johannes Berg41ade002007-12-19 02:03:29 +010096 [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY,
97 .len = WLAN_MAX_KEY_LEN },
98 [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 },
99 [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 },
100 [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG },
Jouni Malinen81962262011-11-02 23:36:31 +0200101 [NL80211_ATTR_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Berge31b8212010-10-05 19:39:30 +0200102 [NL80211_ATTR_KEY_TYPE] = { .type = NLA_U32 },
Johannes Berged1b6cc2007-12-19 02:03:32 +0100103
104 [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 },
105 [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 },
106 [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY,
107 .len = IEEE80211_MAX_DATA_LEN },
108 [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY,
109 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg5727ef12007-12-19 02:03:34 +0100110 [NL80211_ATTR_STA_AID] = { .type = NLA_U16 },
111 [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED },
112 [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
113 [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
114 .len = NL80211_MAX_SUPP_RATES },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100115 [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
Johannes Berg5727ef12007-12-19 02:03:34 +0100116 [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
Johannes Berg0a9542e2008-10-15 11:54:04 +0200117 [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +0100118 [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
119 .len = IEEE80211_MAX_MESH_ID_LEN },
120 [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300121
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -0700122 [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 },
123 [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED },
124
Jouni Malinen9f1ba902008-08-07 20:07:01 +0300125 [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 },
126 [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 },
127 [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 },
Jouni Malinen90c97a02008-10-30 16:59:22 +0200128 [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY,
129 .len = NL80211_MAX_SUPP_RATES },
Helmut Schaa50b12f52010-11-19 12:40:25 +0100130 [NL80211_ATTR_BSS_HT_OPMODE] = { .type = NLA_U16 },
Jouni Malinen36aedc902008-08-25 11:58:58 +0300131
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800132 [NL80211_ATTR_MESH_CONFIG] = { .type = NLA_NESTED },
Javier Cardona15d5dda2011-04-07 15:08:28 -0700133 [NL80211_ATTR_SUPPORT_MESH_AUTH] = { .type = NLA_FLAG },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -0700134
Johannes Berg6c739412011-11-03 09:27:01 +0100135 [NL80211_ATTR_HT_CAPABILITY] = { .len = NL80211_HT_CAPABILITY_LEN },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +0200136
137 [NL80211_ATTR_MGMT_SUBTYPE] = { .type = NLA_U8 },
138 [NL80211_ATTR_IE] = { .type = NLA_BINARY,
139 .len = IEEE80211_MAX_DATA_LEN },
Johannes Berg2a519312009-02-10 21:25:55 +0100140 [NL80211_ATTR_SCAN_FREQUENCIES] = { .type = NLA_NESTED },
141 [NL80211_ATTR_SCAN_SSIDS] = { .type = NLA_NESTED },
Jouni Malinen636a5d32009-03-19 13:39:22 +0200142
143 [NL80211_ATTR_SSID] = { .type = NLA_BINARY,
144 .len = IEEE80211_MAX_SSID_LEN },
145 [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 },
146 [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 },
Johannes Berg04a773a2009-04-19 21:24:32 +0200147 [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG },
Jouni Malinen1965c852009-04-22 21:38:25 +0300148 [NL80211_ATTR_TIMED_OUT] = { .type = NLA_FLAG },
Jouni Malinendc6382c2009-05-06 22:09:37 +0300149 [NL80211_ATTR_USE_MFP] = { .type = NLA_U32 },
Johannes Bergeccb8e82009-05-11 21:57:56 +0300150 [NL80211_ATTR_STA_FLAGS2] = {
151 .len = sizeof(struct nl80211_sta_flag_update),
152 },
Jouni Malinen3f77316c2009-05-11 21:57:57 +0300153 [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG },
Johannes Bergc0692b82010-08-27 14:26:53 +0300154 [NL80211_ATTR_CONTROL_PORT_ETHERTYPE] = { .type = NLA_U16 },
155 [NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT] = { .type = NLA_FLAG },
Samuel Ortizb23aa672009-07-01 21:26:54 +0200156 [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG },
157 [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 },
158 [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 },
Johannes Berg463d0182009-07-14 00:33:35 +0200159 [NL80211_ATTR_PID] = { .type = NLA_U32 },
Felix Fietkau8b787642009-11-10 18:53:10 +0100160 [NL80211_ATTR_4ADDR] = { .type = NLA_U8 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100161 [NL80211_ATTR_PMKID] = { .type = NLA_BINARY,
162 .len = WLAN_PMKID_LEN },
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100163 [NL80211_ATTR_DURATION] = { .type = NLA_U32 },
164 [NL80211_ATTR_COOKIE] = { .type = NLA_U64 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200165 [NL80211_ATTR_TX_RATES] = { .type = NLA_NESTED },
Jouni Malinen026331c2010-02-15 12:53:10 +0200166 [NL80211_ATTR_FRAME] = { .type = NLA_BINARY,
167 .len = IEEE80211_MAX_DATA_LEN },
168 [NL80211_ATTR_FRAME_MATCH] = { .type = NLA_BINARY, },
Kalle Valoffb9eb32010-02-17 17:58:10 +0200169 [NL80211_ATTR_PS_STATE] = { .type = NLA_U32 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +0200170 [NL80211_ATTR_CQM] = { .type = NLA_NESTED, },
Jouni Malinend5cdfac2010-04-04 09:37:19 +0300171 [NL80211_ATTR_LOCAL_STATE_CHANGE] = { .type = NLA_FLAG },
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +0200172 [NL80211_ATTR_AP_ISOLATE] = { .type = NLA_U8 },
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +0300173 [NL80211_ATTR_WIPHY_TX_POWER_SETTING] = { .type = NLA_U32 },
174 [NL80211_ATTR_WIPHY_TX_POWER_LEVEL] = { .type = NLA_U32 },
Johannes Berg2e161f72010-08-12 15:38:38 +0200175 [NL80211_ATTR_FRAME_TYPE] = { .type = NLA_U16 },
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900176 [NL80211_ATTR_WIPHY_ANTENNA_TX] = { .type = NLA_U32 },
177 [NL80211_ATTR_WIPHY_ANTENNA_RX] = { .type = NLA_U32 },
Felix Fietkau885a46d2010-11-11 15:07:22 +0100178 [NL80211_ATTR_MCAST_RATE] = { .type = NLA_U32 },
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100179 [NL80211_ATTR_OFFCHANNEL_TX_OK] = { .type = NLA_FLAG },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100180 [NL80211_ATTR_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200181 [NL80211_ATTR_WOWLAN_TRIGGERS] = { .type = NLA_NESTED },
Javier Cardona9c3990a2011-05-03 16:57:11 -0700182 [NL80211_ATTR_STA_PLINK_STATE] = { .type = NLA_U8 },
Luciano Coelhobbe6ad62011-05-11 17:09:37 +0300183 [NL80211_ATTR_SCHED_SCAN_INTERVAL] = { .type = NLA_U32 },
Johannes Berge5497d72011-07-05 16:35:40 +0200184 [NL80211_ATTR_REKEY_DATA] = { .type = NLA_NESTED },
Johannes Berg34850ab2011-07-18 18:08:35 +0200185 [NL80211_ATTR_SCAN_SUPP_RATES] = { .type = NLA_NESTED },
Jouni Malinen32e9de82011-08-10 23:53:31 +0300186 [NL80211_ATTR_HIDDEN_SSID] = { .type = NLA_U32 },
Jouni Malinen9946ecf2011-08-10 23:55:56 +0300187 [NL80211_ATTR_IE_PROBE_RESP] = { .type = NLA_BINARY,
188 .len = IEEE80211_MAX_DATA_LEN },
189 [NL80211_ATTR_IE_ASSOC_RESP] = { .type = NLA_BINARY,
190 .len = IEEE80211_MAX_DATA_LEN },
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530191 [NL80211_ATTR_ROAM_SUPPORT] = { .type = NLA_FLAG },
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300192 [NL80211_ATTR_SCHED_SCAN_MATCH] = { .type = NLA_NESTED },
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +0530193 [NL80211_ATTR_TX_NO_CCK_RATE] = { .type = NLA_FLAG },
Arik Nemtsov109086c2011-09-28 14:12:50 +0300194 [NL80211_ATTR_TDLS_ACTION] = { .type = NLA_U8 },
195 [NL80211_ATTR_TDLS_DIALOG_TOKEN] = { .type = NLA_U8 },
196 [NL80211_ATTR_TDLS_OPERATION] = { .type = NLA_U8 },
197 [NL80211_ATTR_TDLS_SUPPORT] = { .type = NLA_FLAG },
198 [NL80211_ATTR_TDLS_EXTERNAL_SETUP] = { .type = NLA_FLAG },
Johannes Berge247bd902011-11-04 11:18:21 +0100199 [NL80211_ATTR_DONT_WAIT_FOR_ACK] = { .type = NLA_FLAG },
Johannes Berg55682962007-09-20 13:09:35 -0400200};
201
Johannes Berge31b8212010-10-05 19:39:30 +0200202/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000203static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200204 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200205 [NL80211_KEY_IDX] = { .type = NLA_U8 },
206 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200207 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200208 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
209 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200210 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100211 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
212};
213
214/* policy for the key default flags */
215static const struct nla_policy
216nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
217 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
218 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200219};
220
Johannes Bergff1b6e62011-05-04 15:37:28 +0200221/* policy for WoWLAN attributes */
222static const struct nla_policy
223nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
224 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
225 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
226 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
227 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200228 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
229 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
230 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
231 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200232};
233
Johannes Berge5497d72011-07-05 16:35:40 +0200234/* policy for GTK rekey offload attributes */
235static const struct nla_policy
236nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
237 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
238 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
239 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
240};
241
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300242static const struct nla_policy
243nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
244 [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY,
245 .len = IEEE80211_MAX_SSID_LEN },
246};
247
Holger Schuriga0438972009-11-11 11:30:02 +0100248/* ifidx get helper */
249static int nl80211_get_ifidx(struct netlink_callback *cb)
250{
251 int res;
252
253 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
254 nl80211_fam.attrbuf, nl80211_fam.maxattr,
255 nl80211_policy);
256 if (res)
257 return res;
258
259 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
260 return -EINVAL;
261
262 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
263 if (!res)
264 return -EINVAL;
265 return res;
266}
267
Johannes Berg67748892010-10-04 21:14:06 +0200268static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
269 struct netlink_callback *cb,
270 struct cfg80211_registered_device **rdev,
271 struct net_device **dev)
272{
273 int ifidx = cb->args[0];
274 int err;
275
276 if (!ifidx)
277 ifidx = nl80211_get_ifidx(cb);
278 if (ifidx < 0)
279 return ifidx;
280
281 cb->args[0] = ifidx;
282
283 rtnl_lock();
284
285 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
286 if (!*dev) {
287 err = -ENODEV;
288 goto out_rtnl;
289 }
290
291 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100292 if (IS_ERR(*rdev)) {
293 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200294 goto out_rtnl;
295 }
296
297 return 0;
298 out_rtnl:
299 rtnl_unlock();
300 return err;
301}
302
303static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
304{
305 cfg80211_unlock_rdev(rdev);
306 rtnl_unlock();
307}
308
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100309/* IE validation */
310static bool is_valid_ie_attr(const struct nlattr *attr)
311{
312 const u8 *pos;
313 int len;
314
315 if (!attr)
316 return true;
317
318 pos = nla_data(attr);
319 len = nla_len(attr);
320
321 while (len) {
322 u8 elemlen;
323
324 if (len < 2)
325 return false;
326 len -= 2;
327
328 elemlen = pos[1];
329 if (elemlen > len)
330 return false;
331
332 len -= elemlen;
333 pos += 2 + elemlen;
334 }
335
336 return true;
337}
338
Johannes Berg55682962007-09-20 13:09:35 -0400339/* message building helper */
340static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
341 int flags, u8 cmd)
342{
343 /* since there is no private header just add the generic one */
344 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
345}
346
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400347static int nl80211_msg_put_channel(struct sk_buff *msg,
348 struct ieee80211_channel *chan)
349{
350 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
351 chan->center_freq);
352
353 if (chan->flags & IEEE80211_CHAN_DISABLED)
354 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
355 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
356 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
357 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
358 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
359 if (chan->flags & IEEE80211_CHAN_RADAR)
360 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
361
362 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
363 DBM_TO_MBM(chan->max_power));
364
365 return 0;
366
367 nla_put_failure:
368 return -ENOBUFS;
369}
370
Johannes Berg55682962007-09-20 13:09:35 -0400371/* netlink command implementations */
372
Johannes Bergb9454e82009-07-08 13:29:08 +0200373struct key_parse {
374 struct key_params p;
375 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200376 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200377 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100378 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200379};
380
381static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
382{
383 struct nlattr *tb[NL80211_KEY_MAX + 1];
384 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
385 nl80211_key_policy);
386 if (err)
387 return err;
388
389 k->def = !!tb[NL80211_KEY_DEFAULT];
390 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
391
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100392 if (k->def) {
393 k->def_uni = true;
394 k->def_multi = true;
395 }
396 if (k->defmgmt)
397 k->def_multi = true;
398
Johannes Bergb9454e82009-07-08 13:29:08 +0200399 if (tb[NL80211_KEY_IDX])
400 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
401
402 if (tb[NL80211_KEY_DATA]) {
403 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
404 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
405 }
406
407 if (tb[NL80211_KEY_SEQ]) {
408 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
409 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
410 }
411
412 if (tb[NL80211_KEY_CIPHER])
413 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
414
Johannes Berge31b8212010-10-05 19:39:30 +0200415 if (tb[NL80211_KEY_TYPE]) {
416 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
417 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
418 return -EINVAL;
419 }
420
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100421 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
422 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
423 int err = nla_parse_nested(kdt,
424 NUM_NL80211_KEY_DEFAULT_TYPES - 1,
425 tb[NL80211_KEY_DEFAULT_TYPES],
426 nl80211_key_default_policy);
427 if (err)
428 return err;
429
430 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
431 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
432 }
433
Johannes Bergb9454e82009-07-08 13:29:08 +0200434 return 0;
435}
436
437static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
438{
439 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
440 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
441 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
442 }
443
444 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
445 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
446 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
447 }
448
449 if (info->attrs[NL80211_ATTR_KEY_IDX])
450 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
451
452 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
453 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
454
455 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
456 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
457
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100458 if (k->def) {
459 k->def_uni = true;
460 k->def_multi = true;
461 }
462 if (k->defmgmt)
463 k->def_multi = true;
464
Johannes Berge31b8212010-10-05 19:39:30 +0200465 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
466 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
467 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
468 return -EINVAL;
469 }
470
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100471 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
472 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
473 int err = nla_parse_nested(
474 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
475 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
476 nl80211_key_default_policy);
477 if (err)
478 return err;
479
480 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
481 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
482 }
483
Johannes Bergb9454e82009-07-08 13:29:08 +0200484 return 0;
485}
486
487static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
488{
489 int err;
490
491 memset(k, 0, sizeof(*k));
492 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200493 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200494
495 if (info->attrs[NL80211_ATTR_KEY])
496 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
497 else
498 err = nl80211_parse_key_old(info, k);
499
500 if (err)
501 return err;
502
503 if (k->def && k->defmgmt)
504 return -EINVAL;
505
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100506 if (k->defmgmt) {
507 if (k->def_uni || !k->def_multi)
508 return -EINVAL;
509 }
510
Johannes Bergb9454e82009-07-08 13:29:08 +0200511 if (k->idx != -1) {
512 if (k->defmgmt) {
513 if (k->idx < 4 || k->idx > 5)
514 return -EINVAL;
515 } else if (k->def) {
516 if (k->idx < 0 || k->idx > 3)
517 return -EINVAL;
518 } else {
519 if (k->idx < 0 || k->idx > 5)
520 return -EINVAL;
521 }
522 }
523
524 return 0;
525}
526
Johannes Bergfffd0932009-07-08 14:22:54 +0200527static struct cfg80211_cached_keys *
528nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
529 struct nlattr *keys)
530{
531 struct key_parse parse;
532 struct nlattr *key;
533 struct cfg80211_cached_keys *result;
534 int rem, err, def = 0;
535
536 result = kzalloc(sizeof(*result), GFP_KERNEL);
537 if (!result)
538 return ERR_PTR(-ENOMEM);
539
540 result->def = -1;
541 result->defmgmt = -1;
542
543 nla_for_each_nested(key, keys, rem) {
544 memset(&parse, 0, sizeof(parse));
545 parse.idx = -1;
546
547 err = nl80211_parse_key_new(key, &parse);
548 if (err)
549 goto error;
550 err = -EINVAL;
551 if (!parse.p.key)
552 goto error;
553 if (parse.idx < 0 || parse.idx > 4)
554 goto error;
555 if (parse.def) {
556 if (def)
557 goto error;
558 def = 1;
559 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100560 if (!parse.def_uni || !parse.def_multi)
561 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200562 } else if (parse.defmgmt)
563 goto error;
564 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200565 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200566 if (err)
567 goto error;
568 result->params[parse.idx].cipher = parse.p.cipher;
569 result->params[parse.idx].key_len = parse.p.key_len;
570 result->params[parse.idx].key = result->data[parse.idx];
571 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
572 }
573
574 return result;
575 error:
576 kfree(result);
577 return ERR_PTR(err);
578}
579
580static int nl80211_key_allowed(struct wireless_dev *wdev)
581{
582 ASSERT_WDEV_LOCK(wdev);
583
Johannes Bergfffd0932009-07-08 14:22:54 +0200584 switch (wdev->iftype) {
585 case NL80211_IFTYPE_AP:
586 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200587 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700588 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200589 break;
590 case NL80211_IFTYPE_ADHOC:
591 if (!wdev->current_bss)
592 return -ENOLINK;
593 break;
594 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200595 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200596 if (wdev->sme_state != CFG80211_SME_CONNECTED)
597 return -ENOLINK;
598 break;
599 default:
600 return -EINVAL;
601 }
602
603 return 0;
604}
605
Johannes Berg7527a782011-05-13 10:58:57 +0200606static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
607{
608 struct nlattr *nl_modes = nla_nest_start(msg, attr);
609 int i;
610
611 if (!nl_modes)
612 goto nla_put_failure;
613
614 i = 0;
615 while (ifmodes) {
616 if (ifmodes & 1)
617 NLA_PUT_FLAG(msg, i);
618 ifmodes >>= 1;
619 i++;
620 }
621
622 nla_nest_end(msg, nl_modes);
623 return 0;
624
625nla_put_failure:
626 return -ENOBUFS;
627}
628
629static int nl80211_put_iface_combinations(struct wiphy *wiphy,
630 struct sk_buff *msg)
631{
632 struct nlattr *nl_combis;
633 int i, j;
634
635 nl_combis = nla_nest_start(msg,
636 NL80211_ATTR_INTERFACE_COMBINATIONS);
637 if (!nl_combis)
638 goto nla_put_failure;
639
640 for (i = 0; i < wiphy->n_iface_combinations; i++) {
641 const struct ieee80211_iface_combination *c;
642 struct nlattr *nl_combi, *nl_limits;
643
644 c = &wiphy->iface_combinations[i];
645
646 nl_combi = nla_nest_start(msg, i + 1);
647 if (!nl_combi)
648 goto nla_put_failure;
649
650 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
651 if (!nl_limits)
652 goto nla_put_failure;
653
654 for (j = 0; j < c->n_limits; j++) {
655 struct nlattr *nl_limit;
656
657 nl_limit = nla_nest_start(msg, j + 1);
658 if (!nl_limit)
659 goto nla_put_failure;
660 NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
661 c->limits[j].max);
662 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
663 c->limits[j].types))
664 goto nla_put_failure;
665 nla_nest_end(msg, nl_limit);
666 }
667
668 nla_nest_end(msg, nl_limits);
669
670 if (c->beacon_int_infra_match)
671 NLA_PUT_FLAG(msg,
672 NL80211_IFACE_COMB_STA_AP_BI_MATCH);
673 NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
674 c->num_different_channels);
675 NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
676 c->max_interfaces);
677
678 nla_nest_end(msg, nl_combi);
679 }
680
681 nla_nest_end(msg, nl_combis);
682
683 return 0;
684nla_put_failure:
685 return -ENOBUFS;
686}
687
Johannes Berg55682962007-09-20 13:09:35 -0400688static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
689 struct cfg80211_registered_device *dev)
690{
691 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100692 struct nlattr *nl_bands, *nl_band;
693 struct nlattr *nl_freqs, *nl_freq;
694 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100695 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100696 enum ieee80211_band band;
697 struct ieee80211_channel *chan;
698 struct ieee80211_rate *rate;
699 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200700 const struct ieee80211_txrx_stypes *mgmt_stypes =
701 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400702
703 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
704 if (!hdr)
705 return -1;
706
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500707 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400708 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200709
Johannes Bergf5ea9122009-08-07 16:17:38 +0200710 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
711 cfg80211_rdev_list_generation);
712
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200713 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
714 dev->wiphy.retry_short);
715 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
716 dev->wiphy.retry_long);
717 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
718 dev->wiphy.frag_threshold);
719 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
720 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100721 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
722 dev->wiphy.coverage_class);
Johannes Berg2a519312009-02-10 21:25:55 +0100723 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
724 dev->wiphy.max_scan_ssids);
Luciano Coelho93b6aa62011-07-13 14:57:28 +0300725 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
726 dev->wiphy.max_sched_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200727 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
728 dev->wiphy.max_scan_ie_len);
Luciano Coelho5a865ba2011-07-13 14:57:29 +0300729 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
730 dev->wiphy.max_sched_scan_ie_len);
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300731 NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS,
732 dev->wiphy.max_match_sets);
Johannes Bergee688b002008-01-24 19:38:39 +0100733
Johannes Berge31b8212010-10-05 19:39:30 +0200734 if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
735 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
Javier Cardona15d5dda2011-04-07 15:08:28 -0700736 if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
737 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
Eliad Pellercedb5412011-08-31 11:29:43 +0300738 if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD)
739 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530740 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)
741 NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300742 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)
743 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT);
744 if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)
745 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530746
Johannes Berg25e47c12009-04-02 20:14:06 +0200747 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
748 sizeof(u32) * dev->wiphy.n_cipher_suites,
749 dev->wiphy.cipher_suites);
750
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100751 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
752 dev->wiphy.max_num_pmkids);
753
Johannes Bergc0692b82010-08-27 14:26:53 +0300754 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
755 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
756
Bruno Randolf39fd5de2010-12-16 11:30:28 +0900757 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
758 dev->wiphy.available_antennas_tx);
759 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
760 dev->wiphy.available_antennas_rx);
761
Bruno Randolf7f531e02010-12-16 11:30:22 +0900762 if ((dev->wiphy.available_antennas_tx ||
763 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900764 u32 tx_ant = 0, rx_ant = 0;
765 int res;
766 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
767 if (!res) {
768 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
769 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
770 }
771 }
772
Johannes Berg7527a782011-05-13 10:58:57 +0200773 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
774 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700775 goto nla_put_failure;
776
Johannes Bergee688b002008-01-24 19:38:39 +0100777 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
778 if (!nl_bands)
779 goto nla_put_failure;
780
781 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
782 if (!dev->wiphy.bands[band])
783 continue;
784
785 nl_band = nla_nest_start(msg, band);
786 if (!nl_band)
787 goto nla_put_failure;
788
Johannes Bergd51626d2008-10-09 12:20:13 +0200789 /* add HT info */
790 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
791 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
792 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
793 &dev->wiphy.bands[band]->ht_cap.mcs);
794 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
795 dev->wiphy.bands[band]->ht_cap.cap);
796 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
797 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
798 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
799 dev->wiphy.bands[band]->ht_cap.ampdu_density);
800 }
801
Johannes Bergee688b002008-01-24 19:38:39 +0100802 /* add frequencies */
803 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
804 if (!nl_freqs)
805 goto nla_put_failure;
806
807 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
808 nl_freq = nla_nest_start(msg, i);
809 if (!nl_freq)
810 goto nla_put_failure;
811
812 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100813
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400814 if (nl80211_msg_put_channel(msg, chan))
815 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200816
Johannes Bergee688b002008-01-24 19:38:39 +0100817 nla_nest_end(msg, nl_freq);
818 }
819
820 nla_nest_end(msg, nl_freqs);
821
822 /* add bitrates */
823 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
824 if (!nl_rates)
825 goto nla_put_failure;
826
827 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
828 nl_rate = nla_nest_start(msg, i);
829 if (!nl_rate)
830 goto nla_put_failure;
831
832 rate = &dev->wiphy.bands[band]->bitrates[i];
833 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
834 rate->bitrate);
835 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
836 NLA_PUT_FLAG(msg,
837 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
838
839 nla_nest_end(msg, nl_rate);
840 }
841
842 nla_nest_end(msg, nl_rates);
843
844 nla_nest_end(msg, nl_band);
845 }
846 nla_nest_end(msg, nl_bands);
847
Johannes Berg8fdc6212009-03-14 09:34:01 +0100848 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
849 if (!nl_cmds)
850 goto nla_put_failure;
851
852 i = 0;
853#define CMD(op, n) \
854 do { \
855 if (dev->ops->op) { \
856 i++; \
857 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
858 } \
859 } while (0)
860
861 CMD(add_virtual_intf, NEW_INTERFACE);
862 CMD(change_virtual_intf, SET_INTERFACE);
863 CMD(add_key, NEW_KEY);
864 CMD(add_beacon, NEW_BEACON);
865 CMD(add_station, NEW_STATION);
866 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800867 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100868 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200869 CMD(auth, AUTHENTICATE);
870 CMD(assoc, ASSOCIATE);
871 CMD(deauth, DEAUTHENTICATE);
872 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200873 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100874 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100875 CMD(set_pmksa, SET_PMKSA);
876 CMD(del_pmksa, DEL_PMKSA);
877 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100878 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200879 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200880 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100881 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100882 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200883 i++;
884 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
885 }
Johannes Bergf444de02010-05-05 15:25:02 +0200886 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400887 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300888 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
889 CMD(tdls_mgmt, TDLS_MGMT);
890 CMD(tdls_oper, TDLS_OPER);
891 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300892 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
893 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +0100894 CMD(probe_client, PROBE_CLIENT);
Johannes Berg5e7602302011-11-04 11:18:17 +0100895 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
896 i++;
897 NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
898 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100899
900#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200901
Johannes Berg6829c872009-07-02 09:13:27 +0200902 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200903 i++;
904 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
905 }
906
Johannes Berg6829c872009-07-02 09:13:27 +0200907 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200908 i++;
909 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
910 }
911
Johannes Berg8fdc6212009-03-14 09:34:01 +0100912 nla_nest_end(msg, nl_cmds);
913
Johannes Berga2939112010-12-14 17:54:28 +0100914 if (dev->ops->remain_on_channel)
915 NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
916 dev->wiphy.max_remain_on_channel_duration);
917
Jouni Malinend2da5872011-08-08 12:10:30 +0300918 if (dev->ops->mgmt_tx_cancel_wait)
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100919 NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
920
Johannes Berg2e161f72010-08-12 15:38:38 +0200921 if (mgmt_stypes) {
922 u16 stypes;
923 struct nlattr *nl_ftypes, *nl_ifs;
924 enum nl80211_iftype ift;
925
926 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
927 if (!nl_ifs)
928 goto nla_put_failure;
929
930 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
931 nl_ftypes = nla_nest_start(msg, ift);
932 if (!nl_ftypes)
933 goto nla_put_failure;
934 i = 0;
935 stypes = mgmt_stypes[ift].tx;
936 while (stypes) {
937 if (stypes & 1)
938 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
939 (i << 4) | IEEE80211_FTYPE_MGMT);
940 stypes >>= 1;
941 i++;
942 }
943 nla_nest_end(msg, nl_ftypes);
944 }
945
Johannes Berg74b70a42010-08-24 12:15:53 +0200946 nla_nest_end(msg, nl_ifs);
947
Johannes Berg2e161f72010-08-12 15:38:38 +0200948 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
949 if (!nl_ifs)
950 goto nla_put_failure;
951
952 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
953 nl_ftypes = nla_nest_start(msg, ift);
954 if (!nl_ftypes)
955 goto nla_put_failure;
956 i = 0;
957 stypes = mgmt_stypes[ift].rx;
958 while (stypes) {
959 if (stypes & 1)
960 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
961 (i << 4) | IEEE80211_FTYPE_MGMT);
962 stypes >>= 1;
963 i++;
964 }
965 nla_nest_end(msg, nl_ftypes);
966 }
967 nla_nest_end(msg, nl_ifs);
968 }
969
Johannes Bergff1b6e62011-05-04 15:37:28 +0200970 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
971 struct nlattr *nl_wowlan;
972
973 nl_wowlan = nla_nest_start(msg,
974 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
975 if (!nl_wowlan)
976 goto nla_put_failure;
977
978 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
979 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
980 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
981 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
982 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
983 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +0200984 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
985 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
986 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
987 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
988 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
989 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
990 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
991 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
992 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
993 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +0200994 if (dev->wiphy.wowlan.n_patterns) {
995 struct nl80211_wowlan_pattern_support pat = {
996 .max_patterns = dev->wiphy.wowlan.n_patterns,
997 .min_pattern_len =
998 dev->wiphy.wowlan.pattern_min_len,
999 .max_pattern_len =
1000 dev->wiphy.wowlan.pattern_max_len,
1001 };
1002 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1003 sizeof(pat), &pat);
1004 }
1005
1006 nla_nest_end(msg, nl_wowlan);
1007 }
1008
Johannes Berg7527a782011-05-13 10:58:57 +02001009 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1010 dev->wiphy.software_iftypes))
1011 goto nla_put_failure;
1012
1013 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1014 goto nla_put_failure;
1015
Johannes Berg562a7482011-11-07 12:39:33 +01001016 if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME)
1017 NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME,
1018 dev->wiphy.ap_sme_capa);
1019
Johannes Berg55682962007-09-20 13:09:35 -04001020 return genlmsg_end(msg, hdr);
1021
1022 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001023 genlmsg_cancel(msg, hdr);
1024 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001025}
1026
1027static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1028{
1029 int idx = 0;
1030 int start = cb->args[0];
1031 struct cfg80211_registered_device *dev;
1032
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001033 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001034 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001035 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1036 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001037 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001038 continue;
1039 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
1040 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001041 dev) < 0) {
1042 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001043 break;
Julius Volzb4637272008-07-08 14:02:19 +02001044 }
Johannes Berg55682962007-09-20 13:09:35 -04001045 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001046 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001047
1048 cb->args[0] = idx;
1049
1050 return skb->len;
1051}
1052
1053static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1054{
1055 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001056 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001057
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001058 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001059 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001060 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001061
Johannes Berg4c476992010-10-04 21:36:35 +02001062 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1063 nlmsg_free(msg);
1064 return -ENOBUFS;
1065 }
Johannes Berg55682962007-09-20 13:09:35 -04001066
Johannes Berg134e6372009-07-10 09:51:34 +00001067 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001068}
1069
Jouni Malinen31888482008-10-30 16:59:24 +02001070static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1071 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1072 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1073 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1074 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1075 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1076};
1077
1078static int parse_txq_params(struct nlattr *tb[],
1079 struct ieee80211_txq_params *txq_params)
1080{
1081 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
1082 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1083 !tb[NL80211_TXQ_ATTR_AIFS])
1084 return -EINVAL;
1085
1086 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
1087 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1088 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1089 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1090 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1091
1092 return 0;
1093}
1094
Johannes Bergf444de02010-05-05 15:25:02 +02001095static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1096{
1097 /*
1098 * You can only set the channel explicitly for AP, mesh
1099 * and WDS type interfaces; all others have their channel
1100 * managed via their respective "establish a connection"
1101 * command (connect, join, ...)
1102 *
1103 * Monitors are special as they are normally slaved to
1104 * whatever else is going on, so they behave as though
1105 * you tried setting the wiphy channel itself.
1106 */
1107 return !wdev ||
1108 wdev->iftype == NL80211_IFTYPE_AP ||
1109 wdev->iftype == NL80211_IFTYPE_WDS ||
1110 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001111 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1112 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001113}
1114
1115static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1116 struct wireless_dev *wdev,
1117 struct genl_info *info)
1118{
1119 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1120 u32 freq;
1121 int result;
1122
1123 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1124 return -EINVAL;
1125
1126 if (!nl80211_can_set_dev_channel(wdev))
1127 return -EOPNOTSUPP;
1128
1129 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1130 channel_type = nla_get_u32(info->attrs[
1131 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1132 if (channel_type != NL80211_CHAN_NO_HT &&
1133 channel_type != NL80211_CHAN_HT20 &&
1134 channel_type != NL80211_CHAN_HT40PLUS &&
1135 channel_type != NL80211_CHAN_HT40MINUS)
1136 return -EINVAL;
1137 }
1138
1139 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1140
1141 mutex_lock(&rdev->devlist_mtx);
1142 if (wdev) {
1143 wdev_lock(wdev);
1144 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
1145 wdev_unlock(wdev);
1146 } else {
1147 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
1148 }
1149 mutex_unlock(&rdev->devlist_mtx);
1150
1151 return result;
1152}
1153
1154static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1155{
Johannes Berg4c476992010-10-04 21:36:35 +02001156 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1157 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001158
Johannes Berg4c476992010-10-04 21:36:35 +02001159 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001160}
1161
Bill Jordane8347eb2010-10-01 13:54:28 -04001162static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1163{
Johannes Berg43b19952010-10-07 13:10:30 +02001164 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1165 struct net_device *dev = info->user_ptr[1];
1166 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001167 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001168
1169 if (!info->attrs[NL80211_ATTR_MAC])
1170 return -EINVAL;
1171
Johannes Berg43b19952010-10-07 13:10:30 +02001172 if (netif_running(dev))
1173 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001174
Johannes Berg43b19952010-10-07 13:10:30 +02001175 if (!rdev->ops->set_wds_peer)
1176 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001177
Johannes Berg43b19952010-10-07 13:10:30 +02001178 if (wdev->iftype != NL80211_IFTYPE_WDS)
1179 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001180
1181 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001182 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001183}
1184
1185
Johannes Berg55682962007-09-20 13:09:35 -04001186static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1187{
1188 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001189 struct net_device *netdev = NULL;
1190 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001191 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001192 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001193 u32 changed;
1194 u8 retry_short = 0, retry_long = 0;
1195 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001196 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001197
Johannes Bergf444de02010-05-05 15:25:02 +02001198 /*
1199 * Try to find the wiphy and netdev. Normally this
1200 * function shouldn't need the netdev, but this is
1201 * done for backward compatibility -- previously
1202 * setting the channel was done per wiphy, but now
1203 * it is per netdev. Previous userland like hostapd
1204 * also passed a netdev to set_wiphy, so that it is
1205 * possible to let that go to the right netdev!
1206 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001207 mutex_lock(&cfg80211_mutex);
1208
Johannes Bergf444de02010-05-05 15:25:02 +02001209 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1210 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1211
1212 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1213 if (netdev && netdev->ieee80211_ptr) {
1214 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1215 mutex_lock(&rdev->mtx);
1216 } else
1217 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001218 }
1219
Johannes Bergf444de02010-05-05 15:25:02 +02001220 if (!netdev) {
1221 rdev = __cfg80211_rdev_from_info(info);
1222 if (IS_ERR(rdev)) {
1223 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001224 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001225 }
1226 wdev = NULL;
1227 netdev = NULL;
1228 result = 0;
1229
1230 mutex_lock(&rdev->mtx);
1231 } else if (netif_running(netdev) &&
1232 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
1233 wdev = netdev->ieee80211_ptr;
1234 else
1235 wdev = NULL;
1236
1237 /*
1238 * end workaround code, by now the rdev is available
1239 * and locked, and wdev may or may not be NULL.
1240 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001241
1242 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001243 result = cfg80211_dev_rename(
1244 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001245
1246 mutex_unlock(&cfg80211_mutex);
1247
1248 if (result)
1249 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001250
Jouni Malinen31888482008-10-30 16:59:24 +02001251 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1252 struct ieee80211_txq_params txq_params;
1253 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1254
1255 if (!rdev->ops->set_txq_params) {
1256 result = -EOPNOTSUPP;
1257 goto bad_res;
1258 }
1259
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001260 if (!netdev) {
1261 result = -EINVAL;
1262 goto bad_res;
1263 }
1264
Johannes Berg133a3ff2011-11-03 14:50:13 +01001265 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1266 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1267 result = -EINVAL;
1268 goto bad_res;
1269 }
1270
Jouni Malinen31888482008-10-30 16:59:24 +02001271 nla_for_each_nested(nl_txq_params,
1272 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1273 rem_txq_params) {
1274 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1275 nla_data(nl_txq_params),
1276 nla_len(nl_txq_params),
1277 txq_params_policy);
1278 result = parse_txq_params(tb, &txq_params);
1279 if (result)
1280 goto bad_res;
1281
1282 result = rdev->ops->set_txq_params(&rdev->wiphy,
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001283 netdev,
Jouni Malinen31888482008-10-30 16:59:24 +02001284 &txq_params);
1285 if (result)
1286 goto bad_res;
1287 }
1288 }
1289
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001290 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001291 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001292 if (result)
1293 goto bad_res;
1294 }
1295
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001296 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1297 enum nl80211_tx_power_setting type;
1298 int idx, mbm = 0;
1299
1300 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001301 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001302 goto bad_res;
1303 }
1304
1305 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1306 type = nla_get_u32(info->attrs[idx]);
1307
1308 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1309 (type != NL80211_TX_POWER_AUTOMATIC)) {
1310 result = -EINVAL;
1311 goto bad_res;
1312 }
1313
1314 if (type != NL80211_TX_POWER_AUTOMATIC) {
1315 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1316 mbm = nla_get_u32(info->attrs[idx]);
1317 }
1318
1319 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1320 if (result)
1321 goto bad_res;
1322 }
1323
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001324 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1325 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1326 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001327 if ((!rdev->wiphy.available_antennas_tx &&
1328 !rdev->wiphy.available_antennas_rx) ||
1329 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001330 result = -EOPNOTSUPP;
1331 goto bad_res;
1332 }
1333
1334 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1335 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1336
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001337 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001338 * available antenna masks, except for the "all" mask */
1339 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1340 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001341 result = -EINVAL;
1342 goto bad_res;
1343 }
1344
Bruno Randolf7f531e02010-12-16 11:30:22 +09001345 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1346 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001347
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001348 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1349 if (result)
1350 goto bad_res;
1351 }
1352
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001353 changed = 0;
1354
1355 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1356 retry_short = nla_get_u8(
1357 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1358 if (retry_short == 0) {
1359 result = -EINVAL;
1360 goto bad_res;
1361 }
1362 changed |= WIPHY_PARAM_RETRY_SHORT;
1363 }
1364
1365 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1366 retry_long = nla_get_u8(
1367 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1368 if (retry_long == 0) {
1369 result = -EINVAL;
1370 goto bad_res;
1371 }
1372 changed |= WIPHY_PARAM_RETRY_LONG;
1373 }
1374
1375 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1376 frag_threshold = nla_get_u32(
1377 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1378 if (frag_threshold < 256) {
1379 result = -EINVAL;
1380 goto bad_res;
1381 }
1382 if (frag_threshold != (u32) -1) {
1383 /*
1384 * Fragments (apart from the last one) are required to
1385 * have even length. Make the fragmentation code
1386 * simpler by stripping LSB should someone try to use
1387 * odd threshold value.
1388 */
1389 frag_threshold &= ~0x1;
1390 }
1391 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1392 }
1393
1394 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1395 rts_threshold = nla_get_u32(
1396 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1397 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1398 }
1399
Lukáš Turek81077e82009-12-21 22:50:47 +01001400 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1401 coverage_class = nla_get_u8(
1402 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1403 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1404 }
1405
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001406 if (changed) {
1407 u8 old_retry_short, old_retry_long;
1408 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001409 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001410
1411 if (!rdev->ops->set_wiphy_params) {
1412 result = -EOPNOTSUPP;
1413 goto bad_res;
1414 }
1415
1416 old_retry_short = rdev->wiphy.retry_short;
1417 old_retry_long = rdev->wiphy.retry_long;
1418 old_frag_threshold = rdev->wiphy.frag_threshold;
1419 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001420 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001421
1422 if (changed & WIPHY_PARAM_RETRY_SHORT)
1423 rdev->wiphy.retry_short = retry_short;
1424 if (changed & WIPHY_PARAM_RETRY_LONG)
1425 rdev->wiphy.retry_long = retry_long;
1426 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1427 rdev->wiphy.frag_threshold = frag_threshold;
1428 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1429 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001430 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1431 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001432
1433 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1434 if (result) {
1435 rdev->wiphy.retry_short = old_retry_short;
1436 rdev->wiphy.retry_long = old_retry_long;
1437 rdev->wiphy.frag_threshold = old_frag_threshold;
1438 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001439 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001440 }
1441 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001442
Johannes Berg306d6112008-12-08 12:39:04 +01001443 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001444 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001445 if (netdev)
1446 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001447 return result;
1448}
1449
1450
1451static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001452 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001453 struct net_device *dev)
1454{
1455 void *hdr;
1456
1457 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1458 if (!hdr)
1459 return -1;
1460
1461 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001462 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001463 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001464 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001465
1466 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1467 rdev->devlist_generation ^
1468 (cfg80211_rdev_list_generation << 2));
1469
Johannes Berg55682962007-09-20 13:09:35 -04001470 return genlmsg_end(msg, hdr);
1471
1472 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001473 genlmsg_cancel(msg, hdr);
1474 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001475}
1476
1477static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1478{
1479 int wp_idx = 0;
1480 int if_idx = 0;
1481 int wp_start = cb->args[0];
1482 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001483 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001484 struct wireless_dev *wdev;
1485
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001486 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001487 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1488 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001489 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001490 if (wp_idx < wp_start) {
1491 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001492 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001493 }
Johannes Berg55682962007-09-20 13:09:35 -04001494 if_idx = 0;
1495
Johannes Bergf5ea9122009-08-07 16:17:38 +02001496 mutex_lock(&rdev->devlist_mtx);
1497 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001498 if (if_idx < if_start) {
1499 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001500 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001501 }
Johannes Berg55682962007-09-20 13:09:35 -04001502 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1503 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001504 rdev, wdev->netdev) < 0) {
1505 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001506 goto out;
1507 }
1508 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001509 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001510 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001511
1512 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001513 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001514 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001515 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001516
1517 cb->args[0] = wp_idx;
1518 cb->args[1] = if_idx;
1519
1520 return skb->len;
1521}
1522
1523static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1524{
1525 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001526 struct cfg80211_registered_device *dev = info->user_ptr[0];
1527 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001528
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001529 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001530 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001531 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001532
Johannes Bergd7264052009-04-19 16:23:20 +02001533 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001534 dev, netdev) < 0) {
1535 nlmsg_free(msg);
1536 return -ENOBUFS;
1537 }
Johannes Berg55682962007-09-20 13:09:35 -04001538
Johannes Berg134e6372009-07-10 09:51:34 +00001539 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001540}
1541
Michael Wu66f7ac52008-01-31 19:48:22 +01001542static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1543 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1544 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1545 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1546 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1547 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1548};
1549
1550static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1551{
1552 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1553 int flag;
1554
1555 *mntrflags = 0;
1556
1557 if (!nla)
1558 return -EINVAL;
1559
1560 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1561 nla, mntr_flags_policy))
1562 return -EINVAL;
1563
1564 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1565 if (flags[flag])
1566 *mntrflags |= (1<<flag);
1567
1568 return 0;
1569}
1570
Johannes Berg9bc383d2009-11-19 11:55:19 +01001571static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001572 struct net_device *netdev, u8 use_4addr,
1573 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001574{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001575 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001576 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001577 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001578 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001579 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001580
1581 switch (iftype) {
1582 case NL80211_IFTYPE_AP_VLAN:
1583 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1584 return 0;
1585 break;
1586 case NL80211_IFTYPE_STATION:
1587 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1588 return 0;
1589 break;
1590 default:
1591 break;
1592 }
1593
1594 return -EOPNOTSUPP;
1595}
1596
Johannes Berg55682962007-09-20 13:09:35 -04001597static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1598{
Johannes Berg4c476992010-10-04 21:36:35 +02001599 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001600 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001601 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001602 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001603 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001604 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001605 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001606
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001607 memset(&params, 0, sizeof(params));
1608
Johannes Berg04a773a2009-04-19 21:24:32 +02001609 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001610
Johannes Berg723b0382008-09-16 20:22:09 +02001611 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001612 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001613 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001614 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001615 if (ntype > NL80211_IFTYPE_MAX)
1616 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001617 }
1618
Johannes Berg92ffe052008-09-16 20:39:36 +02001619 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001620 struct wireless_dev *wdev = dev->ieee80211_ptr;
1621
Johannes Berg4c476992010-10-04 21:36:35 +02001622 if (ntype != NL80211_IFTYPE_MESH_POINT)
1623 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001624 if (netif_running(dev))
1625 return -EBUSY;
1626
1627 wdev_lock(wdev);
1628 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1629 IEEE80211_MAX_MESH_ID_LEN);
1630 wdev->mesh_id_up_len =
1631 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1632 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1633 wdev->mesh_id_up_len);
1634 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001635 }
1636
Felix Fietkau8b787642009-11-10 18:53:10 +01001637 if (info->attrs[NL80211_ATTR_4ADDR]) {
1638 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1639 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001640 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001641 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001642 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001643 } else {
1644 params.use_4addr = -1;
1645 }
1646
Johannes Berg92ffe052008-09-16 20:39:36 +02001647 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001648 if (ntype != NL80211_IFTYPE_MONITOR)
1649 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001650 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1651 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001652 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001653 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001654
1655 flags = &_flags;
1656 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001657 }
Johannes Berg3b858752009-03-12 09:55:09 +01001658
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001659 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001660 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001661 else
1662 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001663
Johannes Berg9bc383d2009-11-19 11:55:19 +01001664 if (!err && params.use_4addr != -1)
1665 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1666
Johannes Berg55682962007-09-20 13:09:35 -04001667 return err;
1668}
1669
1670static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1671{
Johannes Berg4c476992010-10-04 21:36:35 +02001672 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001673 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001674 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001675 int err;
1676 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001677 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001678
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001679 memset(&params, 0, sizeof(params));
1680
Johannes Berg55682962007-09-20 13:09:35 -04001681 if (!info->attrs[NL80211_ATTR_IFNAME])
1682 return -EINVAL;
1683
1684 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1685 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1686 if (type > NL80211_IFTYPE_MAX)
1687 return -EINVAL;
1688 }
1689
Johannes Berg79c97e92009-07-07 03:56:12 +02001690 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001691 !(rdev->wiphy.interface_modes & (1 << type)))
1692 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001693
Johannes Berg9bc383d2009-11-19 11:55:19 +01001694 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001695 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001696 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001697 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001698 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001699 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001700
Michael Wu66f7ac52008-01-31 19:48:22 +01001701 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1702 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1703 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001704 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001705 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001706 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001707 if (IS_ERR(dev))
1708 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001709
Johannes Berg29cbe682010-12-03 09:20:44 +01001710 if (type == NL80211_IFTYPE_MESH_POINT &&
1711 info->attrs[NL80211_ATTR_MESH_ID]) {
1712 struct wireless_dev *wdev = dev->ieee80211_ptr;
1713
1714 wdev_lock(wdev);
1715 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1716 IEEE80211_MAX_MESH_ID_LEN);
1717 wdev->mesh_id_up_len =
1718 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1719 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1720 wdev->mesh_id_up_len);
1721 wdev_unlock(wdev);
1722 }
1723
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001724 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001725}
1726
1727static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1728{
Johannes Berg4c476992010-10-04 21:36:35 +02001729 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1730 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001731
Johannes Berg4c476992010-10-04 21:36:35 +02001732 if (!rdev->ops->del_virtual_intf)
1733 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001734
Johannes Berg4c476992010-10-04 21:36:35 +02001735 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001736}
1737
Johannes Berg41ade002007-12-19 02:03:29 +01001738struct get_key_cookie {
1739 struct sk_buff *msg;
1740 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001741 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001742};
1743
1744static void get_key_callback(void *c, struct key_params *params)
1745{
Johannes Bergb9454e82009-07-08 13:29:08 +02001746 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001747 struct get_key_cookie *cookie = c;
1748
1749 if (params->key)
1750 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1751 params->key_len, params->key);
1752
1753 if (params->seq)
1754 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1755 params->seq_len, params->seq);
1756
1757 if (params->cipher)
1758 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1759 params->cipher);
1760
Johannes Bergb9454e82009-07-08 13:29:08 +02001761 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1762 if (!key)
1763 goto nla_put_failure;
1764
1765 if (params->key)
1766 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1767 params->key_len, params->key);
1768
1769 if (params->seq)
1770 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1771 params->seq_len, params->seq);
1772
1773 if (params->cipher)
1774 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1775 params->cipher);
1776
1777 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1778
1779 nla_nest_end(cookie->msg, key);
1780
Johannes Berg41ade002007-12-19 02:03:29 +01001781 return;
1782 nla_put_failure:
1783 cookie->error = 1;
1784}
1785
1786static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1787{
Johannes Berg4c476992010-10-04 21:36:35 +02001788 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001789 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001790 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001791 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001792 const u8 *mac_addr = NULL;
1793 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001794 struct get_key_cookie cookie = {
1795 .error = 0,
1796 };
1797 void *hdr;
1798 struct sk_buff *msg;
1799
1800 if (info->attrs[NL80211_ATTR_KEY_IDX])
1801 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1802
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001803 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001804 return -EINVAL;
1805
1806 if (info->attrs[NL80211_ATTR_MAC])
1807 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1808
Johannes Berge31b8212010-10-05 19:39:30 +02001809 pairwise = !!mac_addr;
1810 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1811 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1812 if (kt >= NUM_NL80211_KEYTYPES)
1813 return -EINVAL;
1814 if (kt != NL80211_KEYTYPE_GROUP &&
1815 kt != NL80211_KEYTYPE_PAIRWISE)
1816 return -EINVAL;
1817 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1818 }
1819
Johannes Berg4c476992010-10-04 21:36:35 +02001820 if (!rdev->ops->get_key)
1821 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001822
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001823 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001824 if (!msg)
1825 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001826
1827 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1828 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001829 if (IS_ERR(hdr))
1830 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001831
1832 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001833 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001834
1835 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1836 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1837 if (mac_addr)
1838 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1839
Johannes Berge31b8212010-10-05 19:39:30 +02001840 if (pairwise && mac_addr &&
1841 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1842 return -ENOENT;
1843
1844 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1845 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001846
1847 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001848 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001849
1850 if (cookie.error)
1851 goto nla_put_failure;
1852
1853 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001854 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001855
1856 nla_put_failure:
1857 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001858 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001859 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001860 return err;
1861}
1862
1863static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1864{
Johannes Berg4c476992010-10-04 21:36:35 +02001865 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001866 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001867 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001868 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001869
Johannes Bergb9454e82009-07-08 13:29:08 +02001870 err = nl80211_parse_key(info, &key);
1871 if (err)
1872 return err;
1873
1874 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001875 return -EINVAL;
1876
Johannes Bergb9454e82009-07-08 13:29:08 +02001877 /* only support setting default key */
1878 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001879 return -EINVAL;
1880
Johannes Bergfffd0932009-07-08 14:22:54 +02001881 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001882
1883 if (key.def) {
1884 if (!rdev->ops->set_default_key) {
1885 err = -EOPNOTSUPP;
1886 goto out;
1887 }
1888
1889 err = nl80211_key_allowed(dev->ieee80211_ptr);
1890 if (err)
1891 goto out;
1892
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001893 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
1894 key.def_uni, key.def_multi);
1895
1896 if (err)
1897 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02001898
Johannes Berg3d23e342009-09-29 23:27:28 +02001899#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001900 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001901#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001902 } else {
1903 if (key.def_uni || !key.def_multi) {
1904 err = -EINVAL;
1905 goto out;
1906 }
1907
1908 if (!rdev->ops->set_default_mgmt_key) {
1909 err = -EOPNOTSUPP;
1910 goto out;
1911 }
1912
1913 err = nl80211_key_allowed(dev->ieee80211_ptr);
1914 if (err)
1915 goto out;
1916
1917 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
1918 dev, key.idx);
1919 if (err)
1920 goto out;
1921
1922#ifdef CONFIG_CFG80211_WEXT
1923 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
1924#endif
1925 }
1926
1927 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02001928 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001929
Johannes Berg41ade002007-12-19 02:03:29 +01001930 return err;
1931}
1932
1933static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1934{
Johannes Berg4c476992010-10-04 21:36:35 +02001935 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02001936 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001937 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02001938 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02001939 const u8 *mac_addr = NULL;
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;
Johannes Berg41ade002007-12-19 02:03:29 +01001944
Johannes Bergb9454e82009-07-08 13:29:08 +02001945 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001946 return -EINVAL;
1947
Johannes Berg41ade002007-12-19 02:03:29 +01001948 if (info->attrs[NL80211_ATTR_MAC])
1949 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1950
Johannes Berge31b8212010-10-05 19:39:30 +02001951 if (key.type == -1) {
1952 if (mac_addr)
1953 key.type = NL80211_KEYTYPE_PAIRWISE;
1954 else
1955 key.type = NL80211_KEYTYPE_GROUP;
1956 }
1957
1958 /* for now */
1959 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
1960 key.type != NL80211_KEYTYPE_GROUP)
1961 return -EINVAL;
1962
Johannes Berg4c476992010-10-04 21:36:35 +02001963 if (!rdev->ops->add_key)
1964 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001965
Johannes Berge31b8212010-10-05 19:39:30 +02001966 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
1967 key.type == NL80211_KEYTYPE_PAIRWISE,
1968 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02001969 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02001970
1971 wdev_lock(dev->ieee80211_ptr);
1972 err = nl80211_key_allowed(dev->ieee80211_ptr);
1973 if (!err)
1974 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02001975 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02001976 mac_addr, &key.p);
1977 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001978
Johannes Berg41ade002007-12-19 02:03:29 +01001979 return err;
1980}
1981
1982static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1983{
Johannes Berg4c476992010-10-04 21:36:35 +02001984 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001985 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001986 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001987 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02001988 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001989
Johannes Bergb9454e82009-07-08 13:29:08 +02001990 err = nl80211_parse_key(info, &key);
1991 if (err)
1992 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001993
1994 if (info->attrs[NL80211_ATTR_MAC])
1995 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1996
Johannes Berge31b8212010-10-05 19:39:30 +02001997 if (key.type == -1) {
1998 if (mac_addr)
1999 key.type = NL80211_KEYTYPE_PAIRWISE;
2000 else
2001 key.type = NL80211_KEYTYPE_GROUP;
2002 }
2003
2004 /* for now */
2005 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2006 key.type != NL80211_KEYTYPE_GROUP)
2007 return -EINVAL;
2008
Johannes Berg4c476992010-10-04 21:36:35 +02002009 if (!rdev->ops->del_key)
2010 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002011
Johannes Bergfffd0932009-07-08 14:22:54 +02002012 wdev_lock(dev->ieee80211_ptr);
2013 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002014
2015 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2016 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2017 err = -ENOENT;
2018
Johannes Bergfffd0932009-07-08 14:22:54 +02002019 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02002020 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
2021 key.type == NL80211_KEYTYPE_PAIRWISE,
2022 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002023
Johannes Berg3d23e342009-09-29 23:27:28 +02002024#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002025 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002026 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002027 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002028 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002029 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2030 }
2031#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002032 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002033
Johannes Berg41ade002007-12-19 02:03:29 +01002034 return err;
2035}
2036
Johannes Berged1b6cc2007-12-19 02:03:32 +01002037static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
2038{
2039 int (*call)(struct wiphy *wiphy, struct net_device *dev,
2040 struct beacon_parameters *info);
Johannes Berg4c476992010-10-04 21:36:35 +02002041 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2042 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002043 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002044 struct beacon_parameters params;
Johannes Berg56d18932011-05-09 18:41:15 +02002045 int haveinfo = 0, err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002046
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002047 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2048 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2049 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2050 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002051 return -EINVAL;
2052
Johannes Berg074ac8d2010-09-16 14:58:22 +02002053 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002054 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2055 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002056
Johannes Berg56d18932011-05-09 18:41:15 +02002057 memset(&params, 0, sizeof(params));
2058
Johannes Berged1b6cc2007-12-19 02:03:32 +01002059 switch (info->genlhdr->cmd) {
2060 case NL80211_CMD_NEW_BEACON:
2061 /* these are required for NEW_BEACON */
2062 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2063 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
Johannes Berg4c476992010-10-04 21:36:35 +02002064 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2065 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002066
Johannes Berg56d18932011-05-09 18:41:15 +02002067 params.interval =
2068 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2069 params.dtim_period =
2070 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2071
2072 err = cfg80211_validate_beacon_int(rdev, params.interval);
2073 if (err)
2074 return err;
2075
Jouni Malinen32e9de82011-08-10 23:53:31 +03002076 /*
2077 * In theory, some of these attributes could be required for
2078 * NEW_BEACON, but since they were not used when the command was
2079 * originally added, keep them optional for old user space
2080 * programs to work with drivers that do not need the additional
2081 * information.
2082 */
2083 if (info->attrs[NL80211_ATTR_SSID]) {
2084 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2085 params.ssid_len =
2086 nla_len(info->attrs[NL80211_ATTR_SSID]);
2087 if (params.ssid_len == 0 ||
2088 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2089 return -EINVAL;
2090 }
2091
2092 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2093 params.hidden_ssid = nla_get_u32(
2094 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2095 if (params.hidden_ssid !=
2096 NL80211_HIDDEN_SSID_NOT_IN_USE &&
2097 params.hidden_ssid !=
2098 NL80211_HIDDEN_SSID_ZERO_LEN &&
2099 params.hidden_ssid !=
2100 NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2101 return -EINVAL;
2102 }
2103
Jouni Malinen5fb628e2011-08-10 23:54:35 +03002104 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2105
2106 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2107 params.auth_type = nla_get_u32(
2108 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2109 if (!nl80211_valid_auth_type(params.auth_type))
2110 return -EINVAL;
2111 } else
2112 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2113
2114 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2115 NL80211_MAX_NR_CIPHER_SUITES);
2116 if (err)
2117 return err;
2118
Johannes Berg79c97e92009-07-07 03:56:12 +02002119 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002120 break;
2121 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02002122 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002123 break;
2124 default:
2125 WARN_ON(1);
Johannes Berg4c476992010-10-04 21:36:35 +02002126 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002127 }
2128
Johannes Berg4c476992010-10-04 21:36:35 +02002129 if (!call)
2130 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002131
Johannes Berged1b6cc2007-12-19 02:03:32 +01002132 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
2133 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2134 params.head_len =
2135 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2136 haveinfo = 1;
2137 }
2138
2139 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
2140 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2141 params.tail_len =
2142 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2143 haveinfo = 1;
2144 }
2145
Johannes Berg4c476992010-10-04 21:36:35 +02002146 if (!haveinfo)
2147 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002148
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002149 if (info->attrs[NL80211_ATTR_IE]) {
2150 params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2151 params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2152 }
2153
2154 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
2155 params.proberesp_ies =
2156 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2157 params.proberesp_ies_len =
2158 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2159 }
2160
2161 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
2162 params.assocresp_ies =
2163 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2164 params.assocresp_ies_len =
2165 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2166 }
2167
Johannes Berg56d18932011-05-09 18:41:15 +02002168 err = call(&rdev->wiphy, dev, &params);
2169 if (!err && params.interval)
2170 wdev->beacon_interval = params.interval;
2171 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002172}
2173
2174static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
2175{
Johannes Berg4c476992010-10-04 21:36:35 +02002176 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2177 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002178 struct wireless_dev *wdev = dev->ieee80211_ptr;
2179 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002180
Johannes Berg4c476992010-10-04 21:36:35 +02002181 if (!rdev->ops->del_beacon)
2182 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002183
Johannes Berg074ac8d2010-09-16 14:58:22 +02002184 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002185 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2186 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002187
Johannes Berg56d18932011-05-09 18:41:15 +02002188 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
2189 if (!err)
2190 wdev->beacon_interval = 0;
2191 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002192}
2193
Johannes Berg5727ef12007-12-19 02:03:34 +01002194static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2195 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2196 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2197 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002198 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002199 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002200};
2201
Johannes Bergeccb8e82009-05-11 21:57:56 +03002202static int parse_station_flags(struct genl_info *info,
2203 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002204{
2205 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002206 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002207 int flag;
2208
Johannes Bergeccb8e82009-05-11 21:57:56 +03002209 /*
2210 * Try parsing the new attribute first so userspace
2211 * can specify both for older kernels.
2212 */
2213 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2214 if (nla) {
2215 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002216
Johannes Bergeccb8e82009-05-11 21:57:56 +03002217 sta_flags = nla_data(nla);
2218 params->sta_flags_mask = sta_flags->mask;
2219 params->sta_flags_set = sta_flags->set;
2220 if ((params->sta_flags_mask |
2221 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2222 return -EINVAL;
2223 return 0;
2224 }
2225
2226 /* if present, parse the old attribute */
2227
2228 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002229 if (!nla)
2230 return 0;
2231
2232 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2233 nla, sta_flags_policy))
2234 return -EINVAL;
2235
Johannes Bergeccb8e82009-05-11 21:57:56 +03002236 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
2237 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002238
2239 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
2240 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03002241 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002242
2243 return 0;
2244}
2245
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002246static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2247 int attr)
2248{
2249 struct nlattr *rate;
2250 u16 bitrate;
2251
2252 rate = nla_nest_start(msg, attr);
2253 if (!rate)
2254 goto nla_put_failure;
2255
2256 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2257 bitrate = cfg80211_calculate_bitrate(info);
2258 if (bitrate > 0)
2259 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
2260
2261 if (info->flags & RATE_INFO_FLAGS_MCS)
2262 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs);
2263 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
2264 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
2265 if (info->flags & RATE_INFO_FLAGS_SHORT_GI)
2266 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
2267
2268 nla_nest_end(msg, rate);
2269 return true;
2270
2271nla_put_failure:
2272 return false;
2273}
2274
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002275static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
2276 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002277 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002278{
2279 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002280 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002281
2282 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2283 if (!hdr)
2284 return -1;
2285
2286 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2287 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
2288
Johannes Bergf5ea9122009-08-07 16:17:38 +02002289 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
2290
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002291 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2292 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002293 goto nla_put_failure;
Mohammed Shafi Shajakhanebe27c92011-04-08 21:24:24 +05302294 if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
2295 NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2296 sinfo->connected_time);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002297 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
2298 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2299 sinfo->inactive_time);
2300 if (sinfo->filled & STATION_INFO_RX_BYTES)
2301 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
2302 sinfo->rx_bytes);
2303 if (sinfo->filled & STATION_INFO_TX_BYTES)
2304 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
2305 sinfo->tx_bytes);
2306 if (sinfo->filled & STATION_INFO_LLID)
2307 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
2308 sinfo->llid);
2309 if (sinfo->filled & STATION_INFO_PLID)
2310 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
2311 sinfo->plid);
2312 if (sinfo->filled & STATION_INFO_PLINK_STATE)
2313 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
2314 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002315 if (sinfo->filled & STATION_INFO_SIGNAL)
2316 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
2317 sinfo->signal);
Bruno Randolf541a45a2010-12-02 19:12:43 +09002318 if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
2319 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2320 sinfo->signal_avg);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002321 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002322 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2323 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002324 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002325 }
2326 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2327 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2328 NL80211_STA_INFO_RX_BITRATE))
2329 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002330 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02002331 if (sinfo->filled & STATION_INFO_RX_PACKETS)
2332 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
2333 sinfo->rx_packets);
2334 if (sinfo->filled & STATION_INFO_TX_PACKETS)
2335 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
2336 sinfo->tx_packets);
Bruno Randolfb206b4e2010-10-06 18:34:12 +09002337 if (sinfo->filled & STATION_INFO_TX_RETRIES)
2338 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES,
2339 sinfo->tx_retries);
2340 if (sinfo->filled & STATION_INFO_TX_FAILED)
2341 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
2342 sinfo->tx_failed);
Paul Stewartf4263c92011-03-31 09:25:41 -07002343 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2344 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2345 if (!bss_param)
2346 goto nla_put_failure;
2347
2348 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
2349 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
2350 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
2351 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
2352 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
2353 NLA_PUT_FLAG(msg,
2354 NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
2355 NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2356 sinfo->bss_param.dtim_period);
2357 NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2358 sinfo->bss_param.beacon_interval);
2359
2360 nla_nest_end(msg, bss_param);
2361 }
Helmut Schaabb6e7532011-10-13 16:30:39 +02002362 if (sinfo->filled & STATION_INFO_STA_FLAGS)
2363 NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS,
2364 sizeof(struct nl80211_sta_flag_update),
2365 &sinfo->sta_flags);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002366 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002367
Felix Fietkau040bdf72011-08-10 19:00:33 -06002368 if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002369 NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2370 sinfo->assoc_req_ies);
2371
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002372 return genlmsg_end(msg, hdr);
2373
2374 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002375 genlmsg_cancel(msg, hdr);
2376 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002377}
2378
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002379static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002380 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002381{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002382 struct station_info sinfo;
2383 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002384 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002385 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002386 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002387 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002388
Johannes Berg67748892010-10-04 21:14:06 +02002389 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2390 if (err)
2391 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002392
2393 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002394 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002395 goto out_err;
2396 }
2397
Johannes Bergbba95fe2008-07-29 13:22:51 +02002398 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002399 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002400 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2401 mac_addr, &sinfo);
2402 if (err == -ENOENT)
2403 break;
2404 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002405 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002406
2407 if (nl80211_send_station(skb,
2408 NETLINK_CB(cb->skb).pid,
2409 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2410 netdev, mac_addr,
2411 &sinfo) < 0)
2412 goto out;
2413
2414 sta_idx++;
2415 }
2416
2417
2418 out:
2419 cb->args[1] = sta_idx;
2420 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002421 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002422 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002423
2424 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002425}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002426
Johannes Berg5727ef12007-12-19 02:03:34 +01002427static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2428{
Johannes Berg4c476992010-10-04 21:36:35 +02002429 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2430 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002431 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002432 struct sk_buff *msg;
2433 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002434 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002435
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002436 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002437
2438 if (!info->attrs[NL80211_ATTR_MAC])
2439 return -EINVAL;
2440
2441 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2442
Johannes Berg4c476992010-10-04 21:36:35 +02002443 if (!rdev->ops->get_station)
2444 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002445
Johannes Berg79c97e92009-07-07 03:56:12 +02002446 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002447 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002448 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002449
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002450 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002451 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002452 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002453
2454 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002455 dev, mac_addr, &sinfo) < 0) {
2456 nlmsg_free(msg);
2457 return -ENOBUFS;
2458 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002459
Johannes Berg4c476992010-10-04 21:36:35 +02002460 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002461}
2462
2463/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002464 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002465 */
Johannes Berg463d0182009-07-14 00:33:35 +02002466static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01002467 struct cfg80211_registered_device *rdev,
2468 struct net_device **vlan)
2469{
Johannes Berg463d0182009-07-14 00:33:35 +02002470 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01002471 *vlan = NULL;
2472
2473 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02002474 *vlan = dev_get_by_index(genl_info_net(info),
2475 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01002476 if (!*vlan)
2477 return -ENODEV;
2478 if (!(*vlan)->ieee80211_ptr)
2479 return -EINVAL;
2480 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
2481 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002482 if (!netif_running(*vlan))
2483 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01002484 }
2485 return 0;
2486}
2487
2488static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2489{
Johannes Berg4c476992010-10-04 21:36:35 +02002490 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002491 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002492 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002493 struct station_parameters params;
2494 u8 *mac_addr = NULL;
2495
2496 memset(&params, 0, sizeof(params));
2497
2498 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002499 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002500
2501 if (info->attrs[NL80211_ATTR_STA_AID])
2502 return -EINVAL;
2503
2504 if (!info->attrs[NL80211_ATTR_MAC])
2505 return -EINVAL;
2506
2507 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2508
2509 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2510 params.supported_rates =
2511 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2512 params.supported_rates_len =
2513 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2514 }
2515
2516 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2517 params.listen_interval =
2518 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2519
Jouni Malinen36aedc902008-08-25 11:58:58 +03002520 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2521 params.ht_capa =
2522 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2523
Johannes Bergeccb8e82009-05-11 21:57:56 +03002524 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002525 return -EINVAL;
2526
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002527 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2528 params.plink_action =
2529 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2530
Javier Cardona9c3990a2011-05-03 16:57:11 -07002531 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2532 params.plink_state =
2533 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2534
Johannes Berg463d0182009-07-14 00:33:35 +02002535 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002536 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02002537 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002538
2539 /* validate settings */
2540 err = 0;
2541
2542 switch (dev->ieee80211_ptr->iftype) {
2543 case NL80211_IFTYPE_AP:
2544 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002545 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002546 /* disallow mesh-specific things */
2547 if (params.plink_action)
2548 err = -EINVAL;
2549 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002550 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002551 case NL80211_IFTYPE_STATION:
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002552 /* disallow things sta doesn't support */
Johannes Berga97f4422009-06-18 17:23:43 +02002553 if (params.plink_action)
2554 err = -EINVAL;
2555 if (params.vlan)
2556 err = -EINVAL;
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002557 if (params.supported_rates &&
2558 !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
Johannes Berga97f4422009-06-18 17:23:43 +02002559 err = -EINVAL;
2560 if (params.ht_capa)
2561 err = -EINVAL;
2562 if (params.listen_interval >= 0)
2563 err = -EINVAL;
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002564 if (params.sta_flags_mask &
2565 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2566 BIT(NL80211_STA_FLAG_TDLS_PEER)))
2567 err = -EINVAL;
2568 /* can't change the TDLS bit */
2569 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
2570 (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)))
Johannes Berga97f4422009-06-18 17:23:43 +02002571 err = -EINVAL;
2572 break;
2573 case NL80211_IFTYPE_MESH_POINT:
2574 /* disallow things mesh doesn't support */
2575 if (params.vlan)
2576 err = -EINVAL;
2577 if (params.ht_capa)
2578 err = -EINVAL;
2579 if (params.listen_interval >= 0)
2580 err = -EINVAL;
Javier Cardonab39c48f2011-04-07 15:08:30 -07002581 if (params.sta_flags_mask &
2582 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002583 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002584 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Berga97f4422009-06-18 17:23:43 +02002585 err = -EINVAL;
2586 break;
2587 default:
2588 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02002589 }
2590
Johannes Berg5727ef12007-12-19 02:03:34 +01002591 if (err)
2592 goto out;
2593
Johannes Berg79c97e92009-07-07 03:56:12 +02002594 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002595 err = -EOPNOTSUPP;
2596 goto out;
2597 }
2598
Johannes Berg79c97e92009-07-07 03:56:12 +02002599 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002600
2601 out:
2602 if (params.vlan)
2603 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002604
Johannes Berg5727ef12007-12-19 02:03:34 +01002605 return err;
2606}
2607
Eliad Pellerc75786c2011-08-23 14:37:46 +03002608static struct nla_policy
2609nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
2610 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
2611 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
2612};
2613
Johannes Berg5727ef12007-12-19 02:03:34 +01002614static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2615{
Johannes Berg4c476992010-10-04 21:36:35 +02002616 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002617 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002618 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002619 struct station_parameters params;
2620 u8 *mac_addr = NULL;
2621
2622 memset(&params, 0, sizeof(params));
2623
2624 if (!info->attrs[NL80211_ATTR_MAC])
2625 return -EINVAL;
2626
Johannes Berg5727ef12007-12-19 02:03:34 +01002627 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2628 return -EINVAL;
2629
2630 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2631 return -EINVAL;
2632
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002633 if (!info->attrs[NL80211_ATTR_STA_AID])
2634 return -EINVAL;
2635
Johannes Berg5727ef12007-12-19 02:03:34 +01002636 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2637 params.supported_rates =
2638 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2639 params.supported_rates_len =
2640 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2641 params.listen_interval =
2642 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002643
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002644 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2645 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2646 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002647
Jouni Malinen36aedc902008-08-25 11:58:58 +03002648 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2649 params.ht_capa =
2650 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002651
Javier Cardona96b78df2011-04-07 15:08:33 -07002652 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2653 params.plink_action =
2654 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2655
Johannes Bergeccb8e82009-05-11 21:57:56 +03002656 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002657 return -EINVAL;
2658
Eliad Pellerc75786c2011-08-23 14:37:46 +03002659 /* parse WME attributes if sta is WME capable */
Eliad Pellercedb5412011-08-31 11:29:43 +03002660 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
Arik Nemtsovcd329842011-09-26 09:36:42 +03002661 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
Eliad Pellerc75786c2011-08-23 14:37:46 +03002662 info->attrs[NL80211_ATTR_STA_WME]) {
2663 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
2664 struct nlattr *nla;
2665
2666 nla = info->attrs[NL80211_ATTR_STA_WME];
2667 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
2668 nl80211_sta_wme_policy);
2669 if (err)
2670 return err;
2671
2672 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
2673 params.uapsd_queues =
2674 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
Johannes Berg4319e192011-09-07 11:50:48 +02002675 if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
2676 return -EINVAL;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002677
2678 if (tb[NL80211_STA_WME_MAX_SP])
2679 params.max_sp =
2680 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
Johannes Berg4319e192011-09-07 11:50:48 +02002681
2682 if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
2683 return -EINVAL;
Johannes Berg3b9ce802011-09-27 20:56:12 +02002684
2685 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002686 }
2687
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002688 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002689 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardona96b78df2011-04-07 15:08:33 -07002690 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002691 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO &&
2692 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
2693 return -EINVAL;
2694
2695 /*
2696 * Only managed stations can add TDLS peers, and only when the
2697 * wiphy supports external TDLS setup.
2698 */
2699 if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION &&
2700 !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
2701 (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
2702 (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
Johannes Berg4c476992010-10-04 21:36:35 +02002703 return -EINVAL;
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002704
Johannes Berg463d0182009-07-14 00:33:35 +02002705 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002706 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002707 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002708
2709 /* validate settings */
2710 err = 0;
2711
Johannes Berg79c97e92009-07-07 03:56:12 +02002712 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002713 err = -EOPNOTSUPP;
2714 goto out;
2715 }
2716
Johannes Berg79c97e92009-07-07 03:56:12 +02002717 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002718
2719 out:
2720 if (params.vlan)
2721 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01002722 return err;
2723}
2724
2725static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2726{
Johannes Berg4c476992010-10-04 21:36:35 +02002727 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2728 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002729 u8 *mac_addr = NULL;
2730
2731 if (info->attrs[NL80211_ATTR_MAC])
2732 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2733
Johannes Berge80cf852009-05-11 14:43:13 +02002734 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002735 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002736 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002737 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2738 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002739
Johannes Berg4c476992010-10-04 21:36:35 +02002740 if (!rdev->ops->del_station)
2741 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01002742
Johannes Berg4c476992010-10-04 21:36:35 +02002743 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002744}
2745
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002746static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2747 int flags, struct net_device *dev,
2748 u8 *dst, u8 *next_hop,
2749 struct mpath_info *pinfo)
2750{
2751 void *hdr;
2752 struct nlattr *pinfoattr;
2753
2754 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2755 if (!hdr)
2756 return -1;
2757
2758 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2759 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2760 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2761
Johannes Bergf5ea9122009-08-07 16:17:38 +02002762 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2763
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002764 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2765 if (!pinfoattr)
2766 goto nla_put_failure;
2767 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2768 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2769 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002770 if (pinfo->filled & MPATH_INFO_SN)
2771 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2772 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002773 if (pinfo->filled & MPATH_INFO_METRIC)
2774 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2775 pinfo->metric);
2776 if (pinfo->filled & MPATH_INFO_EXPTIME)
2777 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2778 pinfo->exptime);
2779 if (pinfo->filled & MPATH_INFO_FLAGS)
2780 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2781 pinfo->flags);
2782 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2783 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2784 pinfo->discovery_timeout);
2785 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2786 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2787 pinfo->discovery_retries);
2788
2789 nla_nest_end(msg, pinfoattr);
2790
2791 return genlmsg_end(msg, hdr);
2792
2793 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002794 genlmsg_cancel(msg, hdr);
2795 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002796}
2797
2798static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002799 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002800{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002801 struct mpath_info pinfo;
2802 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002803 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002804 u8 dst[ETH_ALEN];
2805 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002806 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002807 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002808
Johannes Berg67748892010-10-04 21:14:06 +02002809 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2810 if (err)
2811 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002812
2813 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002814 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002815 goto out_err;
2816 }
2817
Jouni Malineneec60b02009-03-20 21:21:19 +02002818 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2819 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002820 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002821 }
2822
Johannes Bergbba95fe2008-07-29 13:22:51 +02002823 while (1) {
2824 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2825 dst, next_hop, &pinfo);
2826 if (err == -ENOENT)
2827 break;
2828 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002829 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002830
2831 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2832 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2833 netdev, dst, next_hop,
2834 &pinfo) < 0)
2835 goto out;
2836
2837 path_idx++;
2838 }
2839
2840
2841 out:
2842 cb->args[1] = path_idx;
2843 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002844 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002845 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002846 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002847}
2848
2849static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2850{
Johannes Berg4c476992010-10-04 21:36:35 +02002851 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002852 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002853 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002854 struct mpath_info pinfo;
2855 struct sk_buff *msg;
2856 u8 *dst = NULL;
2857 u8 next_hop[ETH_ALEN];
2858
2859 memset(&pinfo, 0, sizeof(pinfo));
2860
2861 if (!info->attrs[NL80211_ATTR_MAC])
2862 return -EINVAL;
2863
2864 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2865
Johannes Berg4c476992010-10-04 21:36:35 +02002866 if (!rdev->ops->get_mpath)
2867 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002868
Johannes Berg4c476992010-10-04 21:36:35 +02002869 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2870 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002871
Johannes Berg79c97e92009-07-07 03:56:12 +02002872 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002873 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002874 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002875
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002876 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002877 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002878 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002879
2880 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002881 dev, dst, next_hop, &pinfo) < 0) {
2882 nlmsg_free(msg);
2883 return -ENOBUFS;
2884 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002885
Johannes Berg4c476992010-10-04 21:36:35 +02002886 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002887}
2888
2889static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2890{
Johannes Berg4c476992010-10-04 21:36:35 +02002891 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2892 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002893 u8 *dst = NULL;
2894 u8 *next_hop = NULL;
2895
2896 if (!info->attrs[NL80211_ATTR_MAC])
2897 return -EINVAL;
2898
2899 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2900 return -EINVAL;
2901
2902 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2903 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2904
Johannes Berg4c476992010-10-04 21:36:35 +02002905 if (!rdev->ops->change_mpath)
2906 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002907
Johannes Berg4c476992010-10-04 21:36:35 +02002908 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2909 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002910
Johannes Berg4c476992010-10-04 21:36:35 +02002911 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002912}
Johannes Berg4c476992010-10-04 21:36:35 +02002913
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002914static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2915{
Johannes Berg4c476992010-10-04 21:36:35 +02002916 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2917 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002918 u8 *dst = NULL;
2919 u8 *next_hop = NULL;
2920
2921 if (!info->attrs[NL80211_ATTR_MAC])
2922 return -EINVAL;
2923
2924 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2925 return -EINVAL;
2926
2927 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2928 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2929
Johannes Berg4c476992010-10-04 21:36:35 +02002930 if (!rdev->ops->add_mpath)
2931 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002932
Johannes Berg4c476992010-10-04 21:36:35 +02002933 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2934 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002935
Johannes Berg4c476992010-10-04 21:36:35 +02002936 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002937}
2938
2939static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2940{
Johannes Berg4c476992010-10-04 21:36:35 +02002941 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2942 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002943 u8 *dst = NULL;
2944
2945 if (info->attrs[NL80211_ATTR_MAC])
2946 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2947
Johannes Berg4c476992010-10-04 21:36:35 +02002948 if (!rdev->ops->del_mpath)
2949 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002950
Johannes Berg4c476992010-10-04 21:36:35 +02002951 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002952}
2953
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002954static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2955{
Johannes Berg4c476992010-10-04 21:36:35 +02002956 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2957 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002958 struct bss_parameters params;
2959
2960 memset(&params, 0, sizeof(params));
2961 /* default to not changing parameters */
2962 params.use_cts_prot = -1;
2963 params.use_short_preamble = -1;
2964 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002965 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01002966 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002967
2968 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2969 params.use_cts_prot =
2970 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2971 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2972 params.use_short_preamble =
2973 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2974 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2975 params.use_short_slot_time =
2976 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002977 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2978 params.basic_rates =
2979 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2980 params.basic_rates_len =
2981 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
2982 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002983 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
2984 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01002985 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
2986 params.ht_opmode =
2987 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002988
Johannes Berg4c476992010-10-04 21:36:35 +02002989 if (!rdev->ops->change_bss)
2990 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002991
Johannes Berg074ac8d2010-09-16 14:58:22 +02002992 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002993 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2994 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002995
Johannes Berg4c476992010-10-04 21:36:35 +02002996 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002997}
2998
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00002999static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003000 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
3001 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
3002 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
3003 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
3004 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
3005 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
3006};
3007
3008static int parse_reg_rule(struct nlattr *tb[],
3009 struct ieee80211_reg_rule *reg_rule)
3010{
3011 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
3012 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
3013
3014 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
3015 return -EINVAL;
3016 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
3017 return -EINVAL;
3018 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
3019 return -EINVAL;
3020 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
3021 return -EINVAL;
3022 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
3023 return -EINVAL;
3024
3025 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
3026
3027 freq_range->start_freq_khz =
3028 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
3029 freq_range->end_freq_khz =
3030 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
3031 freq_range->max_bandwidth_khz =
3032 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
3033
3034 power_rule->max_eirp =
3035 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
3036
3037 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
3038 power_rule->max_antenna_gain =
3039 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
3040
3041 return 0;
3042}
3043
3044static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3045{
3046 int r;
3047 char *data = NULL;
3048
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003049 /*
3050 * You should only get this when cfg80211 hasn't yet initialized
3051 * completely when built-in to the kernel right between the time
3052 * window between nl80211_init() and regulatory_init(), if that is
3053 * even possible.
3054 */
3055 mutex_lock(&cfg80211_mutex);
3056 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003057 mutex_unlock(&cfg80211_mutex);
3058 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003059 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003060 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003061
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003062 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3063 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003064
3065 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3066
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003067 r = regulatory_hint_user(data);
3068
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003069 return r;
3070}
3071
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003072static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003073 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003074{
Johannes Berg4c476992010-10-04 21:36:35 +02003075 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003076 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003077 struct wireless_dev *wdev = dev->ieee80211_ptr;
3078 struct mesh_config cur_params;
3079 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003080 void *hdr;
3081 struct nlattr *pinfoattr;
3082 struct sk_buff *msg;
3083
Johannes Berg29cbe682010-12-03 09:20:44 +01003084 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3085 return -EOPNOTSUPP;
3086
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003087 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02003088 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02003089
Johannes Berg29cbe682010-12-03 09:20:44 +01003090 wdev_lock(wdev);
3091 /* If not connected, get default parameters */
3092 if (!wdev->mesh_id_len)
3093 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
3094 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003095 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003096 &cur_params);
3097 wdev_unlock(wdev);
3098
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003099 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003100 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003101
3102 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003103 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003104 if (!msg)
3105 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003106 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003107 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003108 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003109 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003110 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003111 if (!pinfoattr)
3112 goto nla_put_failure;
3113 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3114 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
3115 cur_params.dot11MeshRetryTimeout);
3116 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3117 cur_params.dot11MeshConfirmTimeout);
3118 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
3119 cur_params.dot11MeshHoldingTimeout);
3120 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
3121 cur_params.dot11MeshMaxPeerLinks);
3122 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
3123 cur_params.dot11MeshMaxRetries);
3124 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
3125 cur_params.dot11MeshTTL);
Javier Cardona45904f22010-12-03 09:20:40 +01003126 NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL,
3127 cur_params.element_ttl);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003128 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3129 cur_params.auto_open_plinks);
3130 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3131 cur_params.dot11MeshHWMPmaxPREQretries);
3132 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3133 cur_params.path_refresh_time);
3134 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3135 cur_params.min_discovery_timeout);
3136 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3137 cur_params.dot11MeshHWMPactivePathTimeout);
3138 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3139 cur_params.dot11MeshHWMPpreqMinInterval);
3140 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3141 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00003142 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3143 cur_params.dot11MeshHWMPRootMode);
Javier Cardona0507e152011-08-09 16:45:10 -07003144 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3145 cur_params.dot11MeshHWMPRannInterval);
Javier Cardona16dd7262011-08-09 16:45:11 -07003146 NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3147 cur_params.dot11MeshGateAnnouncementProtocol);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003148 nla_nest_end(msg, pinfoattr);
3149 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003150 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003151
Johannes Berg3b858752009-03-12 09:55:09 +01003152 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003153 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003154 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003155 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003156 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003157}
3158
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003159static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003160 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3161 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3162 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3163 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3164 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3165 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003166 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003167 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
3168
3169 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3170 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3171 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3172 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3173 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
3174 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07003175 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07003176 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07003177 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003178};
3179
Javier Cardonac80d5452010-12-16 17:37:49 -08003180static const struct nla_policy
3181 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
3182 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3183 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003184 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003185 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Javier Cardonac80d5452010-12-16 17:37:49 -08003186 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003187 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003188};
3189
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003190static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003191 struct mesh_config *cfg,
3192 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003193{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003194 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003195 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003196
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003197#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3198do {\
3199 if (table[attr_num]) {\
3200 cfg->param = nla_fn(table[attr_num]); \
3201 mask |= (1 << (attr_num - 1)); \
3202 } \
3203} while (0);\
3204
3205
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003206 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003207 return -EINVAL;
3208 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003209 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003210 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003211 return -EINVAL;
3212
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003213 /* This makes sure that there aren't more than 32 mesh config
3214 * parameters (otherwise our bitfield scheme would not work.) */
3215 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3216
3217 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003218 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
3219 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
3220 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
3221 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
3222 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
3223 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
3224 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
3225 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
3226 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
3227 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
3228 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
3229 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003230 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
3231 mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003232 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
3233 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
3234 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
3235 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3236 nla_get_u8);
3237 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
3238 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
3239 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
3240 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3241 nla_get_u16);
3242 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
3243 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3244 nla_get_u32);
3245 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
3246 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3247 nla_get_u16);
3248 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3249 dot11MeshHWMPnetDiameterTraversalTime,
3250 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3251 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003252 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3253 dot11MeshHWMPRootMode, mask,
3254 NL80211_MESHCONF_HWMP_ROOTMODE,
3255 nla_get_u8);
Javier Cardona0507e152011-08-09 16:45:10 -07003256 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3257 dot11MeshHWMPRannInterval, mask,
3258 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3259 nla_get_u16);
Javier Cardona16dd7262011-08-09 16:45:11 -07003260 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3261 dot11MeshGateAnnouncementProtocol, mask,
3262 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3263 nla_get_u8);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003264 if (mask_out)
3265 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003266
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003267 return 0;
3268
3269#undef FILL_IN_MESH_PARAM_IF_SET
3270}
3271
Javier Cardonac80d5452010-12-16 17:37:49 -08003272static int nl80211_parse_mesh_setup(struct genl_info *info,
3273 struct mesh_setup *setup)
3274{
3275 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3276
3277 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3278 return -EINVAL;
3279 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3280 info->attrs[NL80211_ATTR_MESH_SETUP],
3281 nl80211_mesh_setup_params_policy))
3282 return -EINVAL;
3283
3284 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3285 setup->path_sel_proto =
3286 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3287 IEEE80211_PATH_PROTOCOL_VENDOR :
3288 IEEE80211_PATH_PROTOCOL_HWMP;
3289
3290 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3291 setup->path_metric =
3292 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3293 IEEE80211_PATH_METRIC_VENDOR :
3294 IEEE80211_PATH_METRIC_AIRTIME;
3295
Javier Cardona581a8b02011-04-07 15:08:27 -07003296
3297 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003298 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003299 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003300 if (!is_valid_ie_attr(ieattr))
3301 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003302 setup->ie = nla_data(ieattr);
3303 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003304 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003305 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3306 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003307
3308 return 0;
3309}
3310
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003311static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003312 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003313{
3314 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3315 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003316 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003317 struct mesh_config cfg;
3318 u32 mask;
3319 int err;
3320
Johannes Berg29cbe682010-12-03 09:20:44 +01003321 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3322 return -EOPNOTSUPP;
3323
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003324 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003325 return -EOPNOTSUPP;
3326
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003327 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003328 if (err)
3329 return err;
3330
Johannes Berg29cbe682010-12-03 09:20:44 +01003331 wdev_lock(wdev);
3332 if (!wdev->mesh_id_len)
3333 err = -ENOLINK;
3334
3335 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003336 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003337 mask, &cfg);
3338
3339 wdev_unlock(wdev);
3340
3341 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003342}
3343
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003344static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3345{
3346 struct sk_buff *msg;
3347 void *hdr = NULL;
3348 struct nlattr *nl_reg_rules;
3349 unsigned int i;
3350 int err = -EINVAL;
3351
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003352 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003353
3354 if (!cfg80211_regdomain)
3355 goto out;
3356
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003357 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003358 if (!msg) {
3359 err = -ENOBUFS;
3360 goto out;
3361 }
3362
3363 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3364 NL80211_CMD_GET_REG);
3365 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003366 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003367
3368 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3369 cfg80211_regdomain->alpha2);
3370
3371 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3372 if (!nl_reg_rules)
3373 goto nla_put_failure;
3374
3375 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3376 struct nlattr *nl_reg_rule;
3377 const struct ieee80211_reg_rule *reg_rule;
3378 const struct ieee80211_freq_range *freq_range;
3379 const struct ieee80211_power_rule *power_rule;
3380
3381 reg_rule = &cfg80211_regdomain->reg_rules[i];
3382 freq_range = &reg_rule->freq_range;
3383 power_rule = &reg_rule->power_rule;
3384
3385 nl_reg_rule = nla_nest_start(msg, i);
3386 if (!nl_reg_rule)
3387 goto nla_put_failure;
3388
3389 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3390 reg_rule->flags);
3391 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3392 freq_range->start_freq_khz);
3393 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3394 freq_range->end_freq_khz);
3395 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3396 freq_range->max_bandwidth_khz);
3397 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3398 power_rule->max_antenna_gain);
3399 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3400 power_rule->max_eirp);
3401
3402 nla_nest_end(msg, nl_reg_rule);
3403 }
3404
3405 nla_nest_end(msg, nl_reg_rules);
3406
3407 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003408 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003409 goto out;
3410
3411nla_put_failure:
3412 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003413put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003414 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003415 err = -EMSGSIZE;
3416out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003417 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003418 return err;
3419}
3420
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003421static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3422{
3423 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3424 struct nlattr *nl_reg_rule;
3425 char *alpha2 = NULL;
3426 int rem_reg_rules = 0, r = 0;
3427 u32 num_rules = 0, rule_idx = 0, size_of_regd;
3428 struct ieee80211_regdomain *rd = NULL;
3429
3430 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3431 return -EINVAL;
3432
3433 if (!info->attrs[NL80211_ATTR_REG_RULES])
3434 return -EINVAL;
3435
3436 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3437
3438 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3439 rem_reg_rules) {
3440 num_rules++;
3441 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003442 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003443 }
3444
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003445 mutex_lock(&cfg80211_mutex);
3446
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003447 if (!reg_is_valid_request(alpha2)) {
3448 r = -EINVAL;
3449 goto bad_reg;
3450 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003451
3452 size_of_regd = sizeof(struct ieee80211_regdomain) +
3453 (num_rules * sizeof(struct ieee80211_reg_rule));
3454
3455 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003456 if (!rd) {
3457 r = -ENOMEM;
3458 goto bad_reg;
3459 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003460
3461 rd->n_reg_rules = num_rules;
3462 rd->alpha2[0] = alpha2[0];
3463 rd->alpha2[1] = alpha2[1];
3464
3465 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3466 rem_reg_rules) {
3467 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3468 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3469 reg_rule_policy);
3470 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3471 if (r)
3472 goto bad_reg;
3473
3474 rule_idx++;
3475
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003476 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3477 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003478 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003479 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003480 }
3481
3482 BUG_ON(rule_idx != num_rules);
3483
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003484 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003485
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003486 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003487
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003488 return r;
3489
Johannes Bergd2372b32008-10-24 20:32:20 +02003490 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003491 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003492 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003493 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003494}
3495
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003496static int validate_scan_freqs(struct nlattr *freqs)
3497{
3498 struct nlattr *attr1, *attr2;
3499 int n_channels = 0, tmp1, tmp2;
3500
3501 nla_for_each_nested(attr1, freqs, tmp1) {
3502 n_channels++;
3503 /*
3504 * Some hardware has a limited channel list for
3505 * scanning, and it is pretty much nonsensical
3506 * to scan for a channel twice, so disallow that
3507 * and don't require drivers to check that the
3508 * channel list they get isn't longer than what
3509 * they can scan, as long as they can scan all
3510 * the channels they registered at once.
3511 */
3512 nla_for_each_nested(attr2, freqs, tmp2)
3513 if (attr1 != attr2 &&
3514 nla_get_u32(attr1) == nla_get_u32(attr2))
3515 return 0;
3516 }
3517
3518 return n_channels;
3519}
3520
Johannes Berg2a519312009-02-10 21:25:55 +01003521static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3522{
Johannes Berg4c476992010-10-04 21:36:35 +02003523 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3524 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003525 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003526 struct nlattr *attr;
3527 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003528 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003529 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003530
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003531 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3532 return -EINVAL;
3533
Johannes Berg79c97e92009-07-07 03:56:12 +02003534 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003535
Johannes Berg4c476992010-10-04 21:36:35 +02003536 if (!rdev->ops->scan)
3537 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003538
Johannes Berg4c476992010-10-04 21:36:35 +02003539 if (rdev->scan_req)
3540 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003541
3542 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003543 n_channels = validate_scan_freqs(
3544 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003545 if (!n_channels)
3546 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003547 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003548 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003549 n_channels = 0;
3550
Johannes Berg2a519312009-02-10 21:25:55 +01003551 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3552 if (wiphy->bands[band])
3553 n_channels += wiphy->bands[band]->n_channels;
3554 }
3555
3556 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3557 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3558 n_ssids++;
3559
Johannes Berg4c476992010-10-04 21:36:35 +02003560 if (n_ssids > wiphy->max_scan_ssids)
3561 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003562
Jouni Malinen70692ad2009-02-16 19:39:13 +02003563 if (info->attrs[NL80211_ATTR_IE])
3564 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3565 else
3566 ie_len = 0;
3567
Johannes Berg4c476992010-10-04 21:36:35 +02003568 if (ie_len > wiphy->max_scan_ie_len)
3569 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003570
Johannes Berg2a519312009-02-10 21:25:55 +01003571 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003572 + sizeof(*request->ssids) * n_ssids
3573 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003574 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003575 if (!request)
3576 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003577
Johannes Berg2a519312009-02-10 21:25:55 +01003578 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003579 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003580 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003581 if (ie_len) {
3582 if (request->ssids)
3583 request->ie = (void *)(request->ssids + n_ssids);
3584 else
3585 request->ie = (void *)(request->channels + n_channels);
3586 }
Johannes Berg2a519312009-02-10 21:25:55 +01003587
Johannes Berg584991d2009-11-02 13:32:03 +01003588 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003589 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3590 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003591 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003592 struct ieee80211_channel *chan;
3593
3594 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3595
3596 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003597 err = -EINVAL;
3598 goto out_free;
3599 }
Johannes Berg584991d2009-11-02 13:32:03 +01003600
3601 /* ignore disabled channels */
3602 if (chan->flags & IEEE80211_CHAN_DISABLED)
3603 continue;
3604
3605 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003606 i++;
3607 }
3608 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003609 enum ieee80211_band band;
3610
Johannes Berg2a519312009-02-10 21:25:55 +01003611 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003612 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3613 int j;
3614 if (!wiphy->bands[band])
3615 continue;
3616 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003617 struct ieee80211_channel *chan;
3618
3619 chan = &wiphy->bands[band]->channels[j];
3620
3621 if (chan->flags & IEEE80211_CHAN_DISABLED)
3622 continue;
3623
3624 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003625 i++;
3626 }
3627 }
3628 }
3629
Johannes Berg584991d2009-11-02 13:32:03 +01003630 if (!i) {
3631 err = -EINVAL;
3632 goto out_free;
3633 }
3634
3635 request->n_channels = i;
3636
Johannes Berg2a519312009-02-10 21:25:55 +01003637 i = 0;
3638 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3639 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003640 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01003641 err = -EINVAL;
3642 goto out_free;
3643 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003644 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01003645 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01003646 i++;
3647 }
3648 }
3649
Jouni Malinen70692ad2009-02-16 19:39:13 +02003650 if (info->attrs[NL80211_ATTR_IE]) {
3651 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003652 memcpy((void *)request->ie,
3653 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003654 request->ie_len);
3655 }
3656
Johannes Berg34850ab2011-07-18 18:08:35 +02003657 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02003658 if (wiphy->bands[i])
3659 request->rates[i] =
3660 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02003661
3662 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
3663 nla_for_each_nested(attr,
3664 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
3665 tmp) {
3666 enum ieee80211_band band = nla_type(attr);
3667
Dan Carpenter84404622011-07-29 11:52:18 +03003668 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02003669 err = -EINVAL;
3670 goto out_free;
3671 }
3672 err = ieee80211_get_ratemask(wiphy->bands[band],
3673 nla_data(attr),
3674 nla_len(attr),
3675 &request->rates[band]);
3676 if (err)
3677 goto out_free;
3678 }
3679 }
3680
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05303681 request->no_cck =
3682 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
3683
Johannes Berg463d0182009-07-14 00:33:35 +02003684 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003685 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003686
Johannes Berg79c97e92009-07-07 03:56:12 +02003687 rdev->scan_req = request;
3688 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003689
Johannes Berg463d0182009-07-14 00:33:35 +02003690 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003691 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003692 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02003693 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01003694 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02003695 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003696 kfree(request);
3697 }
Johannes Berg3b858752009-03-12 09:55:09 +01003698
Johannes Berg2a519312009-02-10 21:25:55 +01003699 return err;
3700}
3701
Luciano Coelho807f8a82011-05-11 17:09:35 +03003702static int nl80211_start_sched_scan(struct sk_buff *skb,
3703 struct genl_info *info)
3704{
3705 struct cfg80211_sched_scan_request *request;
3706 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3707 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003708 struct nlattr *attr;
3709 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003710 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003711 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003712 enum ieee80211_band band;
3713 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003714 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003715
3716 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3717 !rdev->ops->sched_scan_start)
3718 return -EOPNOTSUPP;
3719
3720 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3721 return -EINVAL;
3722
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003723 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
3724 return -EINVAL;
3725
3726 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
3727 if (interval == 0)
3728 return -EINVAL;
3729
Luciano Coelho807f8a82011-05-11 17:09:35 +03003730 wiphy = &rdev->wiphy;
3731
3732 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3733 n_channels = validate_scan_freqs(
3734 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3735 if (!n_channels)
3736 return -EINVAL;
3737 } else {
3738 n_channels = 0;
3739
3740 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3741 if (wiphy->bands[band])
3742 n_channels += wiphy->bands[band]->n_channels;
3743 }
3744
3745 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3746 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3747 tmp)
3748 n_ssids++;
3749
Luciano Coelho93b6aa62011-07-13 14:57:28 +03003750 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003751 return -EINVAL;
3752
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003753 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
3754 nla_for_each_nested(attr,
3755 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
3756 tmp)
3757 n_match_sets++;
3758
3759 if (n_match_sets > wiphy->max_match_sets)
3760 return -EINVAL;
3761
Luciano Coelho807f8a82011-05-11 17:09:35 +03003762 if (info->attrs[NL80211_ATTR_IE])
3763 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3764 else
3765 ie_len = 0;
3766
Luciano Coelho5a865ba2011-07-13 14:57:29 +03003767 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003768 return -EINVAL;
3769
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003770 mutex_lock(&rdev->sched_scan_mtx);
3771
3772 if (rdev->sched_scan_req) {
3773 err = -EINPROGRESS;
3774 goto out;
3775 }
3776
Luciano Coelho807f8a82011-05-11 17:09:35 +03003777 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003778 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003779 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003780 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03003781 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003782 if (!request) {
3783 err = -ENOMEM;
3784 goto out;
3785 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03003786
3787 if (n_ssids)
3788 request->ssids = (void *)&request->channels[n_channels];
3789 request->n_ssids = n_ssids;
3790 if (ie_len) {
3791 if (request->ssids)
3792 request->ie = (void *)(request->ssids + n_ssids);
3793 else
3794 request->ie = (void *)(request->channels + n_channels);
3795 }
3796
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003797 if (n_match_sets) {
3798 if (request->ie)
3799 request->match_sets = (void *)(request->ie + ie_len);
3800 else if (request->ssids)
3801 request->match_sets =
3802 (void *)(request->ssids + n_ssids);
3803 else
3804 request->match_sets =
3805 (void *)(request->channels + n_channels);
3806 }
3807 request->n_match_sets = n_match_sets;
3808
Luciano Coelho807f8a82011-05-11 17:09:35 +03003809 i = 0;
3810 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3811 /* user specified, bail out if channel not found */
3812 nla_for_each_nested(attr,
3813 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
3814 tmp) {
3815 struct ieee80211_channel *chan;
3816
3817 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3818
3819 if (!chan) {
3820 err = -EINVAL;
3821 goto out_free;
3822 }
3823
3824 /* ignore disabled channels */
3825 if (chan->flags & IEEE80211_CHAN_DISABLED)
3826 continue;
3827
3828 request->channels[i] = chan;
3829 i++;
3830 }
3831 } else {
3832 /* all channels */
3833 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3834 int j;
3835 if (!wiphy->bands[band])
3836 continue;
3837 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3838 struct ieee80211_channel *chan;
3839
3840 chan = &wiphy->bands[band]->channels[j];
3841
3842 if (chan->flags & IEEE80211_CHAN_DISABLED)
3843 continue;
3844
3845 request->channels[i] = chan;
3846 i++;
3847 }
3848 }
3849 }
3850
3851 if (!i) {
3852 err = -EINVAL;
3853 goto out_free;
3854 }
3855
3856 request->n_channels = i;
3857
3858 i = 0;
3859 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3860 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3861 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003862 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03003863 err = -EINVAL;
3864 goto out_free;
3865 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003866 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003867 memcpy(request->ssids[i].ssid, nla_data(attr),
3868 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03003869 i++;
3870 }
3871 }
3872
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003873 i = 0;
3874 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
3875 nla_for_each_nested(attr,
3876 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
3877 tmp) {
3878 struct nlattr *ssid;
3879
3880 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
3881 nla_data(attr), nla_len(attr),
3882 nl80211_match_policy);
3883 ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID];
3884 if (ssid) {
3885 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
3886 err = -EINVAL;
3887 goto out_free;
3888 }
3889 memcpy(request->match_sets[i].ssid.ssid,
3890 nla_data(ssid), nla_len(ssid));
3891 request->match_sets[i].ssid.ssid_len =
3892 nla_len(ssid);
3893 }
3894 i++;
3895 }
3896 }
3897
Luciano Coelho807f8a82011-05-11 17:09:35 +03003898 if (info->attrs[NL80211_ATTR_IE]) {
3899 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3900 memcpy((void *)request->ie,
3901 nla_data(info->attrs[NL80211_ATTR_IE]),
3902 request->ie_len);
3903 }
3904
3905 request->dev = dev;
3906 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003907 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003908
3909 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
3910 if (!err) {
3911 rdev->sched_scan_req = request;
3912 nl80211_send_sched_scan(rdev, dev,
3913 NL80211_CMD_START_SCHED_SCAN);
3914 goto out;
3915 }
3916
3917out_free:
3918 kfree(request);
3919out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003920 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003921 return err;
3922}
3923
3924static int nl80211_stop_sched_scan(struct sk_buff *skb,
3925 struct genl_info *info)
3926{
3927 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003928 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003929
3930 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3931 !rdev->ops->sched_scan_stop)
3932 return -EOPNOTSUPP;
3933
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003934 mutex_lock(&rdev->sched_scan_mtx);
3935 err = __cfg80211_stop_sched_scan(rdev, false);
3936 mutex_unlock(&rdev->sched_scan_mtx);
3937
3938 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003939}
3940
Johannes Berg9720bb32011-06-21 09:45:33 +02003941static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
3942 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01003943 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003944 struct wireless_dev *wdev,
3945 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003946{
Johannes Berg48ab9052009-07-10 18:42:31 +02003947 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003948 void *hdr;
3949 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003950 int i;
3951
3952 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003953
Johannes Berg9720bb32011-06-21 09:45:33 +02003954 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01003955 NL80211_CMD_NEW_SCAN_RESULTS);
3956 if (!hdr)
3957 return -1;
3958
Johannes Berg9720bb32011-06-21 09:45:33 +02003959 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
3960
Johannes Bergf5ea9122009-08-07 16:17:38 +02003961 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003962 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003963
3964 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3965 if (!bss)
3966 goto nla_put_failure;
3967 if (!is_zero_ether_addr(res->bssid))
3968 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
3969 if (res->information_elements && res->len_information_elements)
3970 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
3971 res->len_information_elements,
3972 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02003973 if (res->beacon_ies && res->len_beacon_ies &&
3974 res->beacon_ies != res->information_elements)
3975 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
3976 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01003977 if (res->tsf)
3978 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
3979 if (res->beacon_interval)
3980 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
3981 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
3982 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02003983 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
3984 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01003985
Johannes Berg77965c92009-02-18 18:45:06 +01003986 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01003987 case CFG80211_SIGNAL_TYPE_MBM:
3988 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
3989 break;
3990 case CFG80211_SIGNAL_TYPE_UNSPEC:
3991 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
3992 break;
3993 default:
3994 break;
3995 }
3996
Johannes Berg48ab9052009-07-10 18:42:31 +02003997 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02003998 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02003999 case NL80211_IFTYPE_STATION:
4000 if (intbss == wdev->current_bss)
4001 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4002 NL80211_BSS_STATUS_ASSOCIATED);
4003 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
4004 if (intbss != wdev->auth_bsses[i])
4005 continue;
4006 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4007 NL80211_BSS_STATUS_AUTHENTICATED);
4008 break;
4009 }
4010 break;
4011 case NL80211_IFTYPE_ADHOC:
4012 if (intbss == wdev->current_bss)
4013 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4014 NL80211_BSS_STATUS_IBSS_JOINED);
4015 break;
4016 default:
4017 break;
4018 }
4019
Johannes Berg2a519312009-02-10 21:25:55 +01004020 nla_nest_end(msg, bss);
4021
4022 return genlmsg_end(msg, hdr);
4023
4024 nla_put_failure:
4025 genlmsg_cancel(msg, hdr);
4026 return -EMSGSIZE;
4027}
4028
4029static int nl80211_dump_scan(struct sk_buff *skb,
4030 struct netlink_callback *cb)
4031{
Johannes Berg48ab9052009-07-10 18:42:31 +02004032 struct cfg80211_registered_device *rdev;
4033 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01004034 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02004035 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01004036 int start = cb->args[1], idx = 0;
4037 int err;
4038
Johannes Berg67748892010-10-04 21:14:06 +02004039 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
4040 if (err)
4041 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01004042
Johannes Berg48ab9052009-07-10 18:42:31 +02004043 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01004044
Johannes Berg48ab9052009-07-10 18:42:31 +02004045 wdev_lock(wdev);
4046 spin_lock_bh(&rdev->bss_lock);
4047 cfg80211_bss_expire(rdev);
4048
Johannes Berg9720bb32011-06-21 09:45:33 +02004049 cb->seq = rdev->bss_generation;
4050
Johannes Berg48ab9052009-07-10 18:42:31 +02004051 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01004052 if (++idx <= start)
4053 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02004054 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01004055 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02004056 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004057 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02004058 break;
Johannes Berg2a519312009-02-10 21:25:55 +01004059 }
4060 }
4061
Johannes Berg48ab9052009-07-10 18:42:31 +02004062 spin_unlock_bh(&rdev->bss_lock);
4063 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004064
4065 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02004066 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004067
Johannes Berg67748892010-10-04 21:14:06 +02004068 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01004069}
4070
Holger Schurig61fa7132009-11-11 12:25:40 +01004071static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
4072 int flags, struct net_device *dev,
4073 struct survey_info *survey)
4074{
4075 void *hdr;
4076 struct nlattr *infoattr;
4077
Holger Schurig61fa7132009-11-11 12:25:40 +01004078 hdr = nl80211hdr_put(msg, pid, seq, flags,
4079 NL80211_CMD_NEW_SURVEY_RESULTS);
4080 if (!hdr)
4081 return -ENOMEM;
4082
4083 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
4084
4085 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
4086 if (!infoattr)
4087 goto nla_put_failure;
4088
4089 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
4090 survey->channel->center_freq);
4091 if (survey->filled & SURVEY_INFO_NOISE_DBM)
4092 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
4093 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02004094 if (survey->filled & SURVEY_INFO_IN_USE)
4095 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Felix Fietkau8610c292010-10-09 02:39:29 +02004096 if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
4097 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
4098 survey->channel_time);
4099 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
4100 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
4101 survey->channel_time_busy);
4102 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
4103 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
4104 survey->channel_time_ext_busy);
4105 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
4106 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
4107 survey->channel_time_rx);
4108 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
4109 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
4110 survey->channel_time_tx);
Holger Schurig61fa7132009-11-11 12:25:40 +01004111
4112 nla_nest_end(msg, infoattr);
4113
4114 return genlmsg_end(msg, hdr);
4115
4116 nla_put_failure:
4117 genlmsg_cancel(msg, hdr);
4118 return -EMSGSIZE;
4119}
4120
4121static int nl80211_dump_survey(struct sk_buff *skb,
4122 struct netlink_callback *cb)
4123{
4124 struct survey_info survey;
4125 struct cfg80211_registered_device *dev;
4126 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01004127 int survey_idx = cb->args[1];
4128 int res;
4129
Johannes Berg67748892010-10-04 21:14:06 +02004130 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
4131 if (res)
4132 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01004133
4134 if (!dev->ops->dump_survey) {
4135 res = -EOPNOTSUPP;
4136 goto out_err;
4137 }
4138
4139 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004140 struct ieee80211_channel *chan;
4141
Holger Schurig61fa7132009-11-11 12:25:40 +01004142 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
4143 &survey);
4144 if (res == -ENOENT)
4145 break;
4146 if (res)
4147 goto out_err;
4148
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004149 /* Survey without a channel doesn't make sense */
4150 if (!survey.channel) {
4151 res = -EINVAL;
4152 goto out;
4153 }
4154
4155 chan = ieee80211_get_channel(&dev->wiphy,
4156 survey.channel->center_freq);
4157 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
4158 survey_idx++;
4159 continue;
4160 }
4161
Holger Schurig61fa7132009-11-11 12:25:40 +01004162 if (nl80211_send_survey(skb,
4163 NETLINK_CB(cb->skb).pid,
4164 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4165 netdev,
4166 &survey) < 0)
4167 goto out;
4168 survey_idx++;
4169 }
4170
4171 out:
4172 cb->args[1] = survey_idx;
4173 res = skb->len;
4174 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02004175 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01004176 return res;
4177}
4178
Jouni Malinen255e7372009-03-20 21:21:17 +02004179static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
4180{
Samuel Ortizb23aa672009-07-01 21:26:54 +02004181 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02004182}
4183
Samuel Ortizb23aa672009-07-01 21:26:54 +02004184static bool nl80211_valid_wpa_versions(u32 wpa_versions)
4185{
4186 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
4187 NL80211_WPA_VERSION_2));
4188}
4189
Jouni Malinen636a5d32009-03-19 13:39:22 +02004190static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
4191{
Johannes Berg4c476992010-10-04 21:36:35 +02004192 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4193 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004194 struct ieee80211_channel *chan;
4195 const u8 *bssid, *ssid, *ie = NULL;
4196 int err, ssid_len, ie_len = 0;
4197 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02004198 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004199 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004200
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004201 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4202 return -EINVAL;
4203
4204 if (!info->attrs[NL80211_ATTR_MAC])
4205 return -EINVAL;
4206
Jouni Malinen17780922009-03-27 20:52:47 +02004207 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4208 return -EINVAL;
4209
Johannes Berg19957bb2009-07-02 17:20:43 +02004210 if (!info->attrs[NL80211_ATTR_SSID])
4211 return -EINVAL;
4212
4213 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4214 return -EINVAL;
4215
Johannes Bergfffd0932009-07-08 14:22:54 +02004216 err = nl80211_parse_key(info, &key);
4217 if (err)
4218 return err;
4219
4220 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004221 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4222 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004223 if (!key.p.key || !key.p.key_len)
4224 return -EINVAL;
4225 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4226 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4227 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4228 key.p.key_len != WLAN_KEY_LEN_WEP104))
4229 return -EINVAL;
4230 if (key.idx > 4)
4231 return -EINVAL;
4232 } else {
4233 key.p.key_len = 0;
4234 key.p.key = NULL;
4235 }
4236
Johannes Bergafea0b72010-08-10 09:46:42 +02004237 if (key.idx >= 0) {
4238 int i;
4239 bool ok = false;
4240 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4241 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4242 ok = true;
4243 break;
4244 }
4245 }
Johannes Berg4c476992010-10-04 21:36:35 +02004246 if (!ok)
4247 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004248 }
4249
Johannes Berg4c476992010-10-04 21:36:35 +02004250 if (!rdev->ops->auth)
4251 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004252
Johannes Berg074ac8d2010-09-16 14:58:22 +02004253 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004254 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4255 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004256
Johannes Berg19957bb2009-07-02 17:20:43 +02004257 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004258 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004259 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004260 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4261 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004262
Johannes Berg19957bb2009-07-02 17:20:43 +02004263 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4264 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4265
4266 if (info->attrs[NL80211_ATTR_IE]) {
4267 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4268 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4269 }
4270
4271 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004272 if (!nl80211_valid_auth_type(auth_type))
4273 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004274
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004275 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4276
Johannes Berg4c476992010-10-04 21:36:35 +02004277 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4278 ssid, ssid_len, ie, ie_len,
4279 key.p.key, key.p.key_len, key.idx,
4280 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004281}
4282
Johannes Bergc0692b82010-08-27 14:26:53 +03004283static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4284 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004285 struct cfg80211_crypto_settings *settings,
4286 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004287{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004288 memset(settings, 0, sizeof(*settings));
4289
Samuel Ortizb23aa672009-07-01 21:26:54 +02004290 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4291
Johannes Bergc0692b82010-08-27 14:26:53 +03004292 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4293 u16 proto;
4294 proto = nla_get_u16(
4295 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4296 settings->control_port_ethertype = cpu_to_be16(proto);
4297 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4298 proto != ETH_P_PAE)
4299 return -EINVAL;
4300 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4301 settings->control_port_no_encrypt = true;
4302 } else
4303 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4304
Samuel Ortizb23aa672009-07-01 21:26:54 +02004305 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4306 void *data;
4307 int len, i;
4308
4309 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4310 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4311 settings->n_ciphers_pairwise = len / sizeof(u32);
4312
4313 if (len % sizeof(u32))
4314 return -EINVAL;
4315
Johannes Berg3dc27d22009-07-02 21:36:37 +02004316 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004317 return -EINVAL;
4318
4319 memcpy(settings->ciphers_pairwise, data, len);
4320
4321 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004322 if (!cfg80211_supported_cipher_suite(
4323 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004324 settings->ciphers_pairwise[i]))
4325 return -EINVAL;
4326 }
4327
4328 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4329 settings->cipher_group =
4330 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004331 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
4332 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02004333 return -EINVAL;
4334 }
4335
4336 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4337 settings->wpa_versions =
4338 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4339 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4340 return -EINVAL;
4341 }
4342
4343 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4344 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03004345 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004346
4347 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4348 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4349 settings->n_akm_suites = len / sizeof(u32);
4350
4351 if (len % sizeof(u32))
4352 return -EINVAL;
4353
Jouni Malinen1b9ca022011-09-21 16:13:07 +03004354 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4355 return -EINVAL;
4356
Samuel Ortizb23aa672009-07-01 21:26:54 +02004357 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004358 }
4359
4360 return 0;
4361}
4362
Jouni Malinen636a5d32009-03-19 13:39:22 +02004363static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4364{
Johannes Berg4c476992010-10-04 21:36:35 +02004365 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4366 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004367 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004368 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004369 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004370 int err, ssid_len, ie_len = 0;
4371 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004372
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004373 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4374 return -EINVAL;
4375
4376 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004377 !info->attrs[NL80211_ATTR_SSID] ||
4378 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004379 return -EINVAL;
4380
Johannes Berg4c476992010-10-04 21:36:35 +02004381 if (!rdev->ops->assoc)
4382 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004383
Johannes Berg074ac8d2010-09-16 14:58:22 +02004384 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004385 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4386 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004387
Johannes Berg19957bb2009-07-02 17:20:43 +02004388 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004389
Johannes Berg19957bb2009-07-02 17:20:43 +02004390 chan = ieee80211_get_channel(&rdev->wiphy,
4391 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004392 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4393 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004394
Johannes Berg19957bb2009-07-02 17:20:43 +02004395 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4396 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004397
4398 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004399 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4400 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004401 }
4402
Jouni Malinendc6382c2009-05-06 22:09:37 +03004403 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004404 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004405 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004406 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004407 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004408 else if (mfp != NL80211_MFP_NO)
4409 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004410 }
4411
Johannes Berg3e5d7642009-07-07 14:37:26 +02004412 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4413 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4414
Johannes Bergc0692b82010-08-27 14:26:53 +03004415 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004416 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004417 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4418 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02004419 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004420
Jouni Malinen636a5d32009-03-19 13:39:22 +02004421 return err;
4422}
4423
4424static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4425{
Johannes Berg4c476992010-10-04 21:36:35 +02004426 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4427 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004428 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004429 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004430 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004431 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004432
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004433 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4434 return -EINVAL;
4435
4436 if (!info->attrs[NL80211_ATTR_MAC])
4437 return -EINVAL;
4438
4439 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4440 return -EINVAL;
4441
Johannes Berg4c476992010-10-04 21:36:35 +02004442 if (!rdev->ops->deauth)
4443 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004444
Johannes Berg074ac8d2010-09-16 14:58:22 +02004445 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004446 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4447 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004448
Johannes Berg19957bb2009-07-02 17:20:43 +02004449 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004450
Johannes Berg19957bb2009-07-02 17:20:43 +02004451 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4452 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004453 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004454 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004455 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004456
4457 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004458 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4459 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004460 }
4461
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004462 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4463
Johannes Berg4c476992010-10-04 21:36:35 +02004464 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4465 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004466}
4467
4468static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4469{
Johannes Berg4c476992010-10-04 21:36:35 +02004470 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4471 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004472 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004473 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004474 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004475 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004476
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004477 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4478 return -EINVAL;
4479
4480 if (!info->attrs[NL80211_ATTR_MAC])
4481 return -EINVAL;
4482
4483 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4484 return -EINVAL;
4485
Johannes Berg4c476992010-10-04 21:36:35 +02004486 if (!rdev->ops->disassoc)
4487 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004488
Johannes Berg074ac8d2010-09-16 14:58:22 +02004489 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004490 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4491 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004492
Johannes Berg19957bb2009-07-02 17:20:43 +02004493 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004494
Johannes Berg19957bb2009-07-02 17:20:43 +02004495 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4496 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004497 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004498 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004499 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004500
4501 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004502 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4503 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004504 }
4505
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004506 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4507
Johannes Berg4c476992010-10-04 21:36:35 +02004508 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4509 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004510}
4511
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004512static bool
4513nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4514 int mcast_rate[IEEE80211_NUM_BANDS],
4515 int rateval)
4516{
4517 struct wiphy *wiphy = &rdev->wiphy;
4518 bool found = false;
4519 int band, i;
4520
4521 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4522 struct ieee80211_supported_band *sband;
4523
4524 sband = wiphy->bands[band];
4525 if (!sband)
4526 continue;
4527
4528 for (i = 0; i < sband->n_bitrates; i++) {
4529 if (sband->bitrates[i].bitrate == rateval) {
4530 mcast_rate[band] = i + 1;
4531 found = true;
4532 break;
4533 }
4534 }
4535 }
4536
4537 return found;
4538}
4539
Johannes Berg04a773a2009-04-19 21:24:32 +02004540static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4541{
Johannes Berg4c476992010-10-04 21:36:35 +02004542 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4543 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004544 struct cfg80211_ibss_params ibss;
4545 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004546 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004547 int err;
4548
Johannes Berg8e30bc52009-04-22 17:45:38 +02004549 memset(&ibss, 0, sizeof(ibss));
4550
Johannes Berg04a773a2009-04-19 21:24:32 +02004551 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4552 return -EINVAL;
4553
4554 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4555 !info->attrs[NL80211_ATTR_SSID] ||
4556 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4557 return -EINVAL;
4558
Johannes Berg8e30bc52009-04-22 17:45:38 +02004559 ibss.beacon_interval = 100;
4560
4561 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4562 ibss.beacon_interval =
4563 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4564 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4565 return -EINVAL;
4566 }
4567
Johannes Berg4c476992010-10-04 21:36:35 +02004568 if (!rdev->ops->join_ibss)
4569 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004570
Johannes Berg4c476992010-10-04 21:36:35 +02004571 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4572 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004573
Johannes Berg79c97e92009-07-07 03:56:12 +02004574 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004575
Johannes Berg39193492011-09-16 13:45:25 +02004576 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02004577 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02004578
4579 if (!is_valid_ether_addr(ibss.bssid))
4580 return -EINVAL;
4581 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004582 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4583 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4584
4585 if (info->attrs[NL80211_ATTR_IE]) {
4586 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4587 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4588 }
4589
4590 ibss.channel = ieee80211_get_channel(wiphy,
4591 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4592 if (!ibss.channel ||
4593 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004594 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4595 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004596
4597 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004598 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004599
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004600 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4601 u8 *rates =
4602 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4603 int n_rates =
4604 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4605 struct ieee80211_supported_band *sband =
4606 wiphy->bands[ibss.channel->band];
Johannes Berg34850ab2011-07-18 18:08:35 +02004607 int err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004608
Johannes Berg34850ab2011-07-18 18:08:35 +02004609 err = ieee80211_get_ratemask(sband, rates, n_rates,
4610 &ibss.basic_rates);
4611 if (err)
4612 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004613 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004614
4615 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4616 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4617 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4618 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004619
Johannes Berg4c476992010-10-04 21:36:35 +02004620 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4621 connkeys = nl80211_parse_connkeys(rdev,
4622 info->attrs[NL80211_ATTR_KEYS]);
4623 if (IS_ERR(connkeys))
4624 return PTR_ERR(connkeys);
4625 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004626
Johannes Berg4c476992010-10-04 21:36:35 +02004627 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004628 if (err)
4629 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004630 return err;
4631}
4632
4633static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4634{
Johannes Berg4c476992010-10-04 21:36:35 +02004635 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4636 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004637
Johannes Berg4c476992010-10-04 21:36:35 +02004638 if (!rdev->ops->leave_ibss)
4639 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004640
Johannes Berg4c476992010-10-04 21:36:35 +02004641 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4642 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004643
Johannes Berg4c476992010-10-04 21:36:35 +02004644 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004645}
4646
Johannes Bergaff89a92009-07-01 21:26:51 +02004647#ifdef CONFIG_NL80211_TESTMODE
4648static struct genl_multicast_group nl80211_testmode_mcgrp = {
4649 .name = "testmode",
4650};
4651
4652static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4653{
Johannes Berg4c476992010-10-04 21:36:35 +02004654 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02004655 int err;
4656
4657 if (!info->attrs[NL80211_ATTR_TESTDATA])
4658 return -EINVAL;
4659
Johannes Bergaff89a92009-07-01 21:26:51 +02004660 err = -EOPNOTSUPP;
4661 if (rdev->ops->testmode_cmd) {
4662 rdev->testmode_info = info;
4663 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4664 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4665 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4666 rdev->testmode_info = NULL;
4667 }
4668
Johannes Bergaff89a92009-07-01 21:26:51 +02004669 return err;
4670}
4671
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004672static int nl80211_testmode_dump(struct sk_buff *skb,
4673 struct netlink_callback *cb)
4674{
4675 struct cfg80211_registered_device *dev;
4676 int err;
4677 long phy_idx;
4678 void *data = NULL;
4679 int data_len = 0;
4680
4681 if (cb->args[0]) {
4682 /*
4683 * 0 is a valid index, but not valid for args[0],
4684 * so we need to offset by 1.
4685 */
4686 phy_idx = cb->args[0] - 1;
4687 } else {
4688 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
4689 nl80211_fam.attrbuf, nl80211_fam.maxattr,
4690 nl80211_policy);
4691 if (err)
4692 return err;
4693 if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
4694 return -EINVAL;
4695 phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
4696 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
4697 cb->args[1] =
4698 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
4699 }
4700
4701 if (cb->args[1]) {
4702 data = nla_data((void *)cb->args[1]);
4703 data_len = nla_len((void *)cb->args[1]);
4704 }
4705
4706 mutex_lock(&cfg80211_mutex);
4707 dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
4708 if (!dev) {
4709 mutex_unlock(&cfg80211_mutex);
4710 return -ENOENT;
4711 }
4712 cfg80211_lock_rdev(dev);
4713 mutex_unlock(&cfg80211_mutex);
4714
4715 if (!dev->ops->testmode_dump) {
4716 err = -EOPNOTSUPP;
4717 goto out_err;
4718 }
4719
4720 while (1) {
4721 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
4722 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4723 NL80211_CMD_TESTMODE);
4724 struct nlattr *tmdata;
4725
4726 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
4727 genlmsg_cancel(skb, hdr);
4728 break;
4729 }
4730
4731 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4732 if (!tmdata) {
4733 genlmsg_cancel(skb, hdr);
4734 break;
4735 }
4736 err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
4737 data, data_len);
4738 nla_nest_end(skb, tmdata);
4739
4740 if (err == -ENOBUFS || err == -ENOENT) {
4741 genlmsg_cancel(skb, hdr);
4742 break;
4743 } else if (err) {
4744 genlmsg_cancel(skb, hdr);
4745 goto out_err;
4746 }
4747
4748 genlmsg_end(skb, hdr);
4749 }
4750
4751 err = skb->len;
4752 /* see above */
4753 cb->args[0] = phy_idx + 1;
4754 out_err:
4755 cfg80211_unlock_rdev(dev);
4756 return err;
4757}
4758
Johannes Bergaff89a92009-07-01 21:26:51 +02004759static struct sk_buff *
4760__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
4761 int approxlen, u32 pid, u32 seq, gfp_t gfp)
4762{
4763 struct sk_buff *skb;
4764 void *hdr;
4765 struct nlattr *data;
4766
4767 skb = nlmsg_new(approxlen + 100, gfp);
4768 if (!skb)
4769 return NULL;
4770
4771 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
4772 if (!hdr) {
4773 kfree_skb(skb);
4774 return NULL;
4775 }
4776
4777 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4778 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4779
4780 ((void **)skb->cb)[0] = rdev;
4781 ((void **)skb->cb)[1] = hdr;
4782 ((void **)skb->cb)[2] = data;
4783
4784 return skb;
4785
4786 nla_put_failure:
4787 kfree_skb(skb);
4788 return NULL;
4789}
4790
4791struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
4792 int approxlen)
4793{
4794 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4795
4796 if (WARN_ON(!rdev->testmode_info))
4797 return NULL;
4798
4799 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
4800 rdev->testmode_info->snd_pid,
4801 rdev->testmode_info->snd_seq,
4802 GFP_KERNEL);
4803}
4804EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4805
4806int cfg80211_testmode_reply(struct sk_buff *skb)
4807{
4808 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4809 void *hdr = ((void **)skb->cb)[1];
4810 struct nlattr *data = ((void **)skb->cb)[2];
4811
4812 if (WARN_ON(!rdev->testmode_info)) {
4813 kfree_skb(skb);
4814 return -EINVAL;
4815 }
4816
4817 nla_nest_end(skb, data);
4818 genlmsg_end(skb, hdr);
4819 return genlmsg_reply(skb, rdev->testmode_info);
4820}
4821EXPORT_SYMBOL(cfg80211_testmode_reply);
4822
4823struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4824 int approxlen, gfp_t gfp)
4825{
4826 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4827
4828 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4829}
4830EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4831
4832void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4833{
4834 void *hdr = ((void **)skb->cb)[1];
4835 struct nlattr *data = ((void **)skb->cb)[2];
4836
4837 nla_nest_end(skb, data);
4838 genlmsg_end(skb, hdr);
4839 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4840}
4841EXPORT_SYMBOL(cfg80211_testmode_event);
4842#endif
4843
Samuel Ortizb23aa672009-07-01 21:26:54 +02004844static int nl80211_connect(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];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004848 struct cfg80211_connect_params connect;
4849 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004850 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004851 int err;
4852
4853 memset(&connect, 0, sizeof(connect));
4854
4855 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4856 return -EINVAL;
4857
4858 if (!info->attrs[NL80211_ATTR_SSID] ||
4859 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4860 return -EINVAL;
4861
4862 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4863 connect.auth_type =
4864 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4865 if (!nl80211_valid_auth_type(connect.auth_type))
4866 return -EINVAL;
4867 } else
4868 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4869
4870 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4871
Johannes Bergc0692b82010-08-27 14:26:53 +03004872 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004873 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004874 if (err)
4875 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004876
Johannes Berg074ac8d2010-09-16 14:58:22 +02004877 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004878 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4879 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004880
Johannes Berg79c97e92009-07-07 03:56:12 +02004881 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004882
Samuel Ortizb23aa672009-07-01 21:26:54 +02004883 if (info->attrs[NL80211_ATTR_MAC])
4884 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4885 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4886 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4887
4888 if (info->attrs[NL80211_ATTR_IE]) {
4889 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4890 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4891 }
4892
4893 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4894 connect.channel =
4895 ieee80211_get_channel(wiphy,
4896 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4897 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02004898 connect.channel->flags & IEEE80211_CHAN_DISABLED)
4899 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004900 }
4901
Johannes Bergfffd0932009-07-08 14:22:54 +02004902 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4903 connkeys = nl80211_parse_connkeys(rdev,
4904 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02004905 if (IS_ERR(connkeys))
4906 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004907 }
4908
4909 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004910 if (err)
4911 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004912 return err;
4913}
4914
4915static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4916{
Johannes Berg4c476992010-10-04 21:36:35 +02004917 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4918 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004919 u16 reason;
4920
4921 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4922 reason = WLAN_REASON_DEAUTH_LEAVING;
4923 else
4924 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4925
4926 if (reason == 0)
4927 return -EINVAL;
4928
Johannes Berg074ac8d2010-09-16 14:58:22 +02004929 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004930 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4931 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004932
Johannes Berg4c476992010-10-04 21:36:35 +02004933 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004934}
4935
Johannes Berg463d0182009-07-14 00:33:35 +02004936static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4937{
Johannes Berg4c476992010-10-04 21:36:35 +02004938 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02004939 struct net *net;
4940 int err;
4941 u32 pid;
4942
4943 if (!info->attrs[NL80211_ATTR_PID])
4944 return -EINVAL;
4945
4946 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4947
Johannes Berg463d0182009-07-14 00:33:35 +02004948 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02004949 if (IS_ERR(net))
4950 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004951
4952 err = 0;
4953
4954 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02004955 if (!net_eq(wiphy_net(&rdev->wiphy), net))
4956 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02004957
Johannes Berg463d0182009-07-14 00:33:35 +02004958 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004959 return err;
4960}
4961
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004962static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4963{
Johannes Berg4c476992010-10-04 21:36:35 +02004964 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004965 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
4966 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02004967 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004968 struct cfg80211_pmksa pmksa;
4969
4970 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
4971
4972 if (!info->attrs[NL80211_ATTR_MAC])
4973 return -EINVAL;
4974
4975 if (!info->attrs[NL80211_ATTR_PMKID])
4976 return -EINVAL;
4977
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004978 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
4979 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4980
Johannes Berg074ac8d2010-09-16 14:58:22 +02004981 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004982 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4983 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004984
4985 switch (info->genlhdr->cmd) {
4986 case NL80211_CMD_SET_PMKSA:
4987 rdev_ops = rdev->ops->set_pmksa;
4988 break;
4989 case NL80211_CMD_DEL_PMKSA:
4990 rdev_ops = rdev->ops->del_pmksa;
4991 break;
4992 default:
4993 WARN_ON(1);
4994 break;
4995 }
4996
Johannes Berg4c476992010-10-04 21:36:35 +02004997 if (!rdev_ops)
4998 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004999
Johannes Berg4c476992010-10-04 21:36:35 +02005000 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005001}
5002
5003static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
5004{
Johannes Berg4c476992010-10-04 21:36:35 +02005005 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5006 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005007
Johannes Berg074ac8d2010-09-16 14:58:22 +02005008 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005009 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5010 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005011
Johannes Berg4c476992010-10-04 21:36:35 +02005012 if (!rdev->ops->flush_pmksa)
5013 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005014
Johannes Berg4c476992010-10-04 21:36:35 +02005015 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005016}
5017
Arik Nemtsov109086c2011-09-28 14:12:50 +03005018static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
5019{
5020 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5021 struct net_device *dev = info->user_ptr[1];
5022 u8 action_code, dialog_token;
5023 u16 status_code;
5024 u8 *peer;
5025
5026 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5027 !rdev->ops->tdls_mgmt)
5028 return -EOPNOTSUPP;
5029
5030 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
5031 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
5032 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
5033 !info->attrs[NL80211_ATTR_IE] ||
5034 !info->attrs[NL80211_ATTR_MAC])
5035 return -EINVAL;
5036
5037 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5038 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
5039 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
5040 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
5041
5042 return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
5043 dialog_token, status_code,
5044 nla_data(info->attrs[NL80211_ATTR_IE]),
5045 nla_len(info->attrs[NL80211_ATTR_IE]));
5046}
5047
5048static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
5049{
5050 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5051 struct net_device *dev = info->user_ptr[1];
5052 enum nl80211_tdls_operation operation;
5053 u8 *peer;
5054
5055 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5056 !rdev->ops->tdls_oper)
5057 return -EOPNOTSUPP;
5058
5059 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
5060 !info->attrs[NL80211_ATTR_MAC])
5061 return -EINVAL;
5062
5063 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
5064 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5065
5066 return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
5067}
5068
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005069static int nl80211_remain_on_channel(struct sk_buff *skb,
5070 struct genl_info *info)
5071{
Johannes Berg4c476992010-10-04 21:36:35 +02005072 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5073 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005074 struct ieee80211_channel *chan;
5075 struct sk_buff *msg;
5076 void *hdr;
5077 u64 cookie;
5078 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
5079 u32 freq, duration;
5080 int err;
5081
5082 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5083 !info->attrs[NL80211_ATTR_DURATION])
5084 return -EINVAL;
5085
5086 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5087
5088 /*
5089 * We should be on that channel for at least one jiffie,
5090 * and more than 5 seconds seems excessive.
5091 */
Johannes Berga2939112010-12-14 17:54:28 +01005092 if (!duration || !msecs_to_jiffies(duration) ||
5093 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005094 return -EINVAL;
5095
Johannes Berg4c476992010-10-04 21:36:35 +02005096 if (!rdev->ops->remain_on_channel)
5097 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005098
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005099 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5100 channel_type = nla_get_u32(
5101 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5102 if (channel_type != NL80211_CHAN_NO_HT &&
5103 channel_type != NL80211_CHAN_HT20 &&
5104 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005105 channel_type != NL80211_CHAN_HT40MINUS)
5106 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005107 }
5108
5109 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5110 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005111 if (chan == NULL)
5112 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005113
5114 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005115 if (!msg)
5116 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005117
5118 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5119 NL80211_CMD_REMAIN_ON_CHANNEL);
5120
5121 if (IS_ERR(hdr)) {
5122 err = PTR_ERR(hdr);
5123 goto free_msg;
5124 }
5125
5126 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
5127 channel_type, duration, &cookie);
5128
5129 if (err)
5130 goto free_msg;
5131
5132 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5133
5134 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005135
5136 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005137
5138 nla_put_failure:
5139 err = -ENOBUFS;
5140 free_msg:
5141 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005142 return err;
5143}
5144
5145static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
5146 struct genl_info *info)
5147{
Johannes Berg4c476992010-10-04 21:36:35 +02005148 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5149 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005150 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005151
5152 if (!info->attrs[NL80211_ATTR_COOKIE])
5153 return -EINVAL;
5154
Johannes Berg4c476992010-10-04 21:36:35 +02005155 if (!rdev->ops->cancel_remain_on_channel)
5156 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005157
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005158 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5159
Johannes Berg4c476992010-10-04 21:36:35 +02005160 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005161}
5162
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005163static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
5164 u8 *rates, u8 rates_len)
5165{
5166 u8 i;
5167 u32 mask = 0;
5168
5169 for (i = 0; i < rates_len; i++) {
5170 int rate = (rates[i] & 0x7f) * 5;
5171 int ridx;
5172 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
5173 struct ieee80211_rate *srate =
5174 &sband->bitrates[ridx];
5175 if (rate == srate->bitrate) {
5176 mask |= 1 << ridx;
5177 break;
5178 }
5179 }
5180 if (ridx == sband->n_bitrates)
5181 return 0; /* rate not found */
5182 }
5183
5184 return mask;
5185}
5186
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00005187static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005188 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
5189 .len = NL80211_MAX_SUPP_RATES },
5190};
5191
5192static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
5193 struct genl_info *info)
5194{
5195 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02005196 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005197 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02005198 int rem, i;
5199 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005200 struct nlattr *tx_rates;
5201 struct ieee80211_supported_band *sband;
5202
5203 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
5204 return -EINVAL;
5205
Johannes Berg4c476992010-10-04 21:36:35 +02005206 if (!rdev->ops->set_bitrate_mask)
5207 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005208
5209 memset(&mask, 0, sizeof(mask));
5210 /* Default to all rates enabled */
5211 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
5212 sband = rdev->wiphy.bands[i];
5213 mask.control[i].legacy =
5214 sband ? (1 << sband->n_bitrates) - 1 : 0;
5215 }
5216
5217 /*
5218 * The nested attribute uses enum nl80211_band as the index. This maps
5219 * directly to the enum ieee80211_band values used in cfg80211.
5220 */
5221 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
5222 {
5223 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02005224 if (band < 0 || band >= IEEE80211_NUM_BANDS)
5225 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005226 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02005227 if (sband == NULL)
5228 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005229 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
5230 nla_len(tx_rates), nl80211_txattr_policy);
5231 if (tb[NL80211_TXRATE_LEGACY]) {
5232 mask.control[band].legacy = rateset_to_mask(
5233 sband,
5234 nla_data(tb[NL80211_TXRATE_LEGACY]),
5235 nla_len(tb[NL80211_TXRATE_LEGACY]));
Johannes Berg4c476992010-10-04 21:36:35 +02005236 if (mask.control[band].legacy == 0)
5237 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005238 }
5239 }
5240
Johannes Berg4c476992010-10-04 21:36:35 +02005241 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005242}
5243
Johannes Berg2e161f72010-08-12 15:38:38 +02005244static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005245{
Johannes Berg4c476992010-10-04 21:36:35 +02005246 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5247 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02005248 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02005249
5250 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
5251 return -EINVAL;
5252
Johannes Berg2e161f72010-08-12 15:38:38 +02005253 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
5254 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02005255
Johannes Berg9d38d852010-06-09 17:20:33 +02005256 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005257 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005258 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5259 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5260 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005261 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005262 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5263 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005264
5265 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005266 if (!rdev->ops->mgmt_tx)
5267 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005268
Johannes Berg4c476992010-10-04 21:36:35 +02005269 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02005270 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005271 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5272 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005273}
5274
Johannes Berg2e161f72010-08-12 15:38:38 +02005275static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005276{
Johannes Berg4c476992010-10-04 21:36:35 +02005277 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5278 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005279 struct ieee80211_channel *chan;
5280 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005281 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005282 u32 freq;
5283 int err;
5284 void *hdr;
5285 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01005286 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005287 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01005288 bool offchan, no_cck, dont_wait_for_ack;
5289
5290 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02005291
5292 if (!info->attrs[NL80211_ATTR_FRAME] ||
5293 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5294 return -EINVAL;
5295
Johannes Berg4c476992010-10-04 21:36:35 +02005296 if (!rdev->ops->mgmt_tx)
5297 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005298
Johannes Berg9d38d852010-06-09 17:20:33 +02005299 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005300 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005301 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5302 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5303 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005304 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005305 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5306 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005307
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005308 if (info->attrs[NL80211_ATTR_DURATION]) {
5309 if (!rdev->ops->mgmt_tx_cancel_wait)
5310 return -EINVAL;
5311 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5312 }
5313
Jouni Malinen026331c2010-02-15 12:53:10 +02005314 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5315 channel_type = nla_get_u32(
5316 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5317 if (channel_type != NL80211_CHAN_NO_HT &&
5318 channel_type != NL80211_CHAN_HT20 &&
5319 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005320 channel_type != NL80211_CHAN_HT40MINUS)
5321 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005322 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005323 }
5324
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005325 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5326
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305327 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5328
Jouni Malinen026331c2010-02-15 12:53:10 +02005329 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5330 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005331 if (chan == NULL)
5332 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005333
Johannes Berge247bd902011-11-04 11:18:21 +01005334 if (!dont_wait_for_ack) {
5335 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5336 if (!msg)
5337 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005338
Johannes Berge247bd902011-11-04 11:18:21 +01005339 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5340 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005341
Johannes Berge247bd902011-11-04 11:18:21 +01005342 if (IS_ERR(hdr)) {
5343 err = PTR_ERR(hdr);
5344 goto free_msg;
5345 }
Jouni Malinen026331c2010-02-15 12:53:10 +02005346 }
Johannes Berge247bd902011-11-04 11:18:21 +01005347
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005348 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5349 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02005350 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5351 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01005352 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005353 if (err)
5354 goto free_msg;
5355
Johannes Berge247bd902011-11-04 11:18:21 +01005356 if (msg) {
5357 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005358
Johannes Berge247bd902011-11-04 11:18:21 +01005359 genlmsg_end(msg, hdr);
5360 return genlmsg_reply(msg, info);
5361 }
5362
5363 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02005364
5365 nla_put_failure:
5366 err = -ENOBUFS;
5367 free_msg:
5368 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005369 return err;
5370}
5371
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005372static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5373{
5374 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5375 struct net_device *dev = info->user_ptr[1];
5376 u64 cookie;
5377
5378 if (!info->attrs[NL80211_ATTR_COOKIE])
5379 return -EINVAL;
5380
5381 if (!rdev->ops->mgmt_tx_cancel_wait)
5382 return -EOPNOTSUPP;
5383
5384 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5385 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5386 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5387 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5388 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5389 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5390 return -EOPNOTSUPP;
5391
5392 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5393
5394 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5395}
5396
Kalle Valoffb9eb32010-02-17 17:58:10 +02005397static int nl80211_set_power_save(struct sk_buff *skb, struct genl_info *info)
5398{
Johannes Berg4c476992010-10-04 21:36:35 +02005399 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005400 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005401 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005402 u8 ps_state;
5403 bool state;
5404 int err;
5405
Johannes Berg4c476992010-10-04 21:36:35 +02005406 if (!info->attrs[NL80211_ATTR_PS_STATE])
5407 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005408
5409 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5410
Johannes Berg4c476992010-10-04 21:36:35 +02005411 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5412 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005413
5414 wdev = dev->ieee80211_ptr;
5415
Johannes Berg4c476992010-10-04 21:36:35 +02005416 if (!rdev->ops->set_power_mgmt)
5417 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005418
5419 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5420
5421 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005422 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005423
Johannes Berg4c476992010-10-04 21:36:35 +02005424 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5425 wdev->ps_timeout);
5426 if (!err)
5427 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005428 return err;
5429}
5430
5431static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5432{
Johannes Berg4c476992010-10-04 21:36:35 +02005433 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005434 enum nl80211_ps_state ps_state;
5435 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005436 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005437 struct sk_buff *msg;
5438 void *hdr;
5439 int err;
5440
Kalle Valoffb9eb32010-02-17 17:58:10 +02005441 wdev = dev->ieee80211_ptr;
5442
Johannes Berg4c476992010-10-04 21:36:35 +02005443 if (!rdev->ops->set_power_mgmt)
5444 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005445
5446 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005447 if (!msg)
5448 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005449
5450 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5451 NL80211_CMD_GET_POWER_SAVE);
5452 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005453 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005454 goto free_msg;
5455 }
5456
5457 if (wdev->ps)
5458 ps_state = NL80211_PS_ENABLED;
5459 else
5460 ps_state = NL80211_PS_DISABLED;
5461
5462 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5463
5464 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005465 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005466
Johannes Berg4c476992010-10-04 21:36:35 +02005467 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005468 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005469 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005470 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005471 return err;
5472}
5473
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005474static struct nla_policy
5475nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5476 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5477 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5478 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5479};
5480
5481static int nl80211_set_cqm_rssi(struct genl_info *info,
5482 s32 threshold, u32 hysteresis)
5483{
Johannes Berg4c476992010-10-04 21:36:35 +02005484 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005485 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005486 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005487
5488 if (threshold > 0)
5489 return -EINVAL;
5490
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005491 wdev = dev->ieee80211_ptr;
5492
Johannes Berg4c476992010-10-04 21:36:35 +02005493 if (!rdev->ops->set_cqm_rssi_config)
5494 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005495
Johannes Berg074ac8d2010-09-16 14:58:22 +02005496 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005497 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
5498 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005499
Johannes Berg4c476992010-10-04 21:36:35 +02005500 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5501 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005502}
5503
5504static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5505{
5506 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5507 struct nlattr *cqm;
5508 int err;
5509
5510 cqm = info->attrs[NL80211_ATTR_CQM];
5511 if (!cqm) {
5512 err = -EINVAL;
5513 goto out;
5514 }
5515
5516 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5517 nl80211_attr_cqm_policy);
5518 if (err)
5519 goto out;
5520
5521 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5522 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5523 s32 threshold;
5524 u32 hysteresis;
5525 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5526 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5527 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5528 } else
5529 err = -EINVAL;
5530
5531out:
5532 return err;
5533}
5534
Johannes Berg29cbe682010-12-03 09:20:44 +01005535static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
5536{
5537 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5538 struct net_device *dev = info->user_ptr[1];
5539 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08005540 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01005541 int err;
5542
5543 /* start with default */
5544 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08005545 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01005546
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005547 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01005548 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005549 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01005550 if (err)
5551 return err;
5552 }
5553
5554 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
5555 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
5556 return -EINVAL;
5557
Javier Cardonac80d5452010-12-16 17:37:49 -08005558 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
5559 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
5560
5561 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
5562 /* parse additional setup parameters if given */
5563 err = nl80211_parse_mesh_setup(info, &setup);
5564 if (err)
5565 return err;
5566 }
5567
5568 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01005569}
5570
5571static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
5572{
5573 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5574 struct net_device *dev = info->user_ptr[1];
5575
5576 return cfg80211_leave_mesh(rdev, dev);
5577}
5578
Johannes Bergff1b6e62011-05-04 15:37:28 +02005579static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
5580{
5581 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5582 struct sk_buff *msg;
5583 void *hdr;
5584
5585 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5586 return -EOPNOTSUPP;
5587
5588 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5589 if (!msg)
5590 return -ENOMEM;
5591
5592 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5593 NL80211_CMD_GET_WOWLAN);
5594 if (!hdr)
5595 goto nla_put_failure;
5596
5597 if (rdev->wowlan) {
5598 struct nlattr *nl_wowlan;
5599
5600 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
5601 if (!nl_wowlan)
5602 goto nla_put_failure;
5603
5604 if (rdev->wowlan->any)
5605 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
5606 if (rdev->wowlan->disconnect)
5607 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
5608 if (rdev->wowlan->magic_pkt)
5609 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02005610 if (rdev->wowlan->gtk_rekey_failure)
5611 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
5612 if (rdev->wowlan->eap_identity_req)
5613 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
5614 if (rdev->wowlan->four_way_handshake)
5615 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
5616 if (rdev->wowlan->rfkill_release)
5617 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02005618 if (rdev->wowlan->n_patterns) {
5619 struct nlattr *nl_pats, *nl_pat;
5620 int i, pat_len;
5621
5622 nl_pats = nla_nest_start(msg,
5623 NL80211_WOWLAN_TRIG_PKT_PATTERN);
5624 if (!nl_pats)
5625 goto nla_put_failure;
5626
5627 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
5628 nl_pat = nla_nest_start(msg, i + 1);
5629 if (!nl_pat)
5630 goto nla_put_failure;
5631 pat_len = rdev->wowlan->patterns[i].pattern_len;
5632 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
5633 DIV_ROUND_UP(pat_len, 8),
5634 rdev->wowlan->patterns[i].mask);
5635 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
5636 pat_len,
5637 rdev->wowlan->patterns[i].pattern);
5638 nla_nest_end(msg, nl_pat);
5639 }
5640 nla_nest_end(msg, nl_pats);
5641 }
5642
5643 nla_nest_end(msg, nl_wowlan);
5644 }
5645
5646 genlmsg_end(msg, hdr);
5647 return genlmsg_reply(msg, info);
5648
5649nla_put_failure:
5650 nlmsg_free(msg);
5651 return -ENOBUFS;
5652}
5653
5654static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
5655{
5656 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5657 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
5658 struct cfg80211_wowlan no_triggers = {};
5659 struct cfg80211_wowlan new_triggers = {};
5660 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
5661 int err, i;
5662
5663 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5664 return -EOPNOTSUPP;
5665
5666 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
5667 goto no_triggers;
5668
5669 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
5670 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5671 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5672 nl80211_wowlan_policy);
5673 if (err)
5674 return err;
5675
5676 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
5677 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
5678 return -EINVAL;
5679 new_triggers.any = true;
5680 }
5681
5682 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
5683 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
5684 return -EINVAL;
5685 new_triggers.disconnect = true;
5686 }
5687
5688 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
5689 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
5690 return -EINVAL;
5691 new_triggers.magic_pkt = true;
5692 }
5693
Johannes Berg77dbbb12011-07-13 10:48:55 +02005694 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
5695 return -EINVAL;
5696
5697 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
5698 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
5699 return -EINVAL;
5700 new_triggers.gtk_rekey_failure = true;
5701 }
5702
5703 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
5704 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
5705 return -EINVAL;
5706 new_triggers.eap_identity_req = true;
5707 }
5708
5709 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
5710 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
5711 return -EINVAL;
5712 new_triggers.four_way_handshake = true;
5713 }
5714
5715 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
5716 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
5717 return -EINVAL;
5718 new_triggers.rfkill_release = true;
5719 }
5720
Johannes Bergff1b6e62011-05-04 15:37:28 +02005721 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
5722 struct nlattr *pat;
5723 int n_patterns = 0;
5724 int rem, pat_len, mask_len;
5725 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
5726
5727 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5728 rem)
5729 n_patterns++;
5730 if (n_patterns > wowlan->n_patterns)
5731 return -EINVAL;
5732
5733 new_triggers.patterns = kcalloc(n_patterns,
5734 sizeof(new_triggers.patterns[0]),
5735 GFP_KERNEL);
5736 if (!new_triggers.patterns)
5737 return -ENOMEM;
5738
5739 new_triggers.n_patterns = n_patterns;
5740 i = 0;
5741
5742 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5743 rem) {
5744 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
5745 nla_data(pat), nla_len(pat), NULL);
5746 err = -EINVAL;
5747 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
5748 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
5749 goto error;
5750 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
5751 mask_len = DIV_ROUND_UP(pat_len, 8);
5752 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
5753 mask_len)
5754 goto error;
5755 if (pat_len > wowlan->pattern_max_len ||
5756 pat_len < wowlan->pattern_min_len)
5757 goto error;
5758
5759 new_triggers.patterns[i].mask =
5760 kmalloc(mask_len + pat_len, GFP_KERNEL);
5761 if (!new_triggers.patterns[i].mask) {
5762 err = -ENOMEM;
5763 goto error;
5764 }
5765 new_triggers.patterns[i].pattern =
5766 new_triggers.patterns[i].mask + mask_len;
5767 memcpy(new_triggers.patterns[i].mask,
5768 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
5769 mask_len);
5770 new_triggers.patterns[i].pattern_len = pat_len;
5771 memcpy(new_triggers.patterns[i].pattern,
5772 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
5773 pat_len);
5774 i++;
5775 }
5776 }
5777
5778 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
5779 struct cfg80211_wowlan *ntrig;
5780 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
5781 GFP_KERNEL);
5782 if (!ntrig) {
5783 err = -ENOMEM;
5784 goto error;
5785 }
5786 cfg80211_rdev_free_wowlan(rdev);
5787 rdev->wowlan = ntrig;
5788 } else {
5789 no_triggers:
5790 cfg80211_rdev_free_wowlan(rdev);
5791 rdev->wowlan = NULL;
5792 }
5793
5794 return 0;
5795 error:
5796 for (i = 0; i < new_triggers.n_patterns; i++)
5797 kfree(new_triggers.patterns[i].mask);
5798 kfree(new_triggers.patterns);
5799 return err;
5800}
5801
Johannes Berge5497d72011-07-05 16:35:40 +02005802static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
5803{
5804 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5805 struct net_device *dev = info->user_ptr[1];
5806 struct wireless_dev *wdev = dev->ieee80211_ptr;
5807 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
5808 struct cfg80211_gtk_rekey_data rekey_data;
5809 int err;
5810
5811 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
5812 return -EINVAL;
5813
5814 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
5815 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
5816 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
5817 nl80211_rekey_policy);
5818 if (err)
5819 return err;
5820
5821 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
5822 return -ERANGE;
5823 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
5824 return -ERANGE;
5825 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
5826 return -ERANGE;
5827
5828 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
5829 NL80211_KEK_LEN);
5830 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
5831 NL80211_KCK_LEN);
5832 memcpy(rekey_data.replay_ctr,
5833 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
5834 NL80211_REPLAY_CTR_LEN);
5835
5836 wdev_lock(wdev);
5837 if (!wdev->current_bss) {
5838 err = -ENOTCONN;
5839 goto out;
5840 }
5841
5842 if (!rdev->ops->set_rekey_data) {
5843 err = -EOPNOTSUPP;
5844 goto out;
5845 }
5846
5847 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
5848 out:
5849 wdev_unlock(wdev);
5850 return err;
5851}
5852
Johannes Berg28946da2011-11-04 11:18:12 +01005853static int nl80211_register_unexpected_frame(struct sk_buff *skb,
5854 struct genl_info *info)
5855{
5856 struct net_device *dev = info->user_ptr[1];
5857 struct wireless_dev *wdev = dev->ieee80211_ptr;
5858
5859 if (wdev->iftype != NL80211_IFTYPE_AP &&
5860 wdev->iftype != NL80211_IFTYPE_P2P_GO)
5861 return -EINVAL;
5862
5863 if (wdev->ap_unexpected_nlpid)
5864 return -EBUSY;
5865
5866 wdev->ap_unexpected_nlpid = info->snd_pid;
5867 return 0;
5868}
5869
Johannes Berg7f6cf312011-11-04 11:18:15 +01005870static int nl80211_probe_client(struct sk_buff *skb,
5871 struct genl_info *info)
5872{
5873 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5874 struct net_device *dev = info->user_ptr[1];
5875 struct wireless_dev *wdev = dev->ieee80211_ptr;
5876 struct sk_buff *msg;
5877 void *hdr;
5878 const u8 *addr;
5879 u64 cookie;
5880 int err;
5881
5882 if (wdev->iftype != NL80211_IFTYPE_AP &&
5883 wdev->iftype != NL80211_IFTYPE_P2P_GO)
5884 return -EOPNOTSUPP;
5885
5886 if (!info->attrs[NL80211_ATTR_MAC])
5887 return -EINVAL;
5888
5889 if (!rdev->ops->probe_client)
5890 return -EOPNOTSUPP;
5891
5892 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5893 if (!msg)
5894 return -ENOMEM;
5895
5896 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5897 NL80211_CMD_PROBE_CLIENT);
5898
5899 if (IS_ERR(hdr)) {
5900 err = PTR_ERR(hdr);
5901 goto free_msg;
5902 }
5903
5904 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
5905
5906 err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
5907 if (err)
5908 goto free_msg;
5909
5910 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5911
5912 genlmsg_end(msg, hdr);
5913
5914 return genlmsg_reply(msg, info);
5915
5916 nla_put_failure:
5917 err = -ENOBUFS;
5918 free_msg:
5919 nlmsg_free(msg);
5920 return err;
5921}
5922
Johannes Berg5e7602302011-11-04 11:18:17 +01005923static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
5924{
5925 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5926
5927 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
5928 return -EOPNOTSUPP;
5929
5930 if (rdev->ap_beacons_nlpid)
5931 return -EBUSY;
5932
5933 rdev->ap_beacons_nlpid = info->snd_pid;
5934
5935 return 0;
5936}
5937
Johannes Berg4c476992010-10-04 21:36:35 +02005938#define NL80211_FLAG_NEED_WIPHY 0x01
5939#define NL80211_FLAG_NEED_NETDEV 0x02
5940#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02005941#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
5942#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
5943 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02005944
5945static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
5946 struct genl_info *info)
5947{
5948 struct cfg80211_registered_device *rdev;
5949 struct net_device *dev;
5950 int err;
5951 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
5952
5953 if (rtnl)
5954 rtnl_lock();
5955
5956 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
5957 rdev = cfg80211_get_dev_from_info(info);
5958 if (IS_ERR(rdev)) {
5959 if (rtnl)
5960 rtnl_unlock();
5961 return PTR_ERR(rdev);
5962 }
5963 info->user_ptr[0] = rdev;
5964 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
5965 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
5966 if (err) {
5967 if (rtnl)
5968 rtnl_unlock();
5969 return err;
5970 }
Johannes Berg41265712010-10-04 21:14:05 +02005971 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
5972 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02005973 cfg80211_unlock_rdev(rdev);
5974 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02005975 if (rtnl)
5976 rtnl_unlock();
5977 return -ENETDOWN;
5978 }
Johannes Berg4c476992010-10-04 21:36:35 +02005979 info->user_ptr[0] = rdev;
5980 info->user_ptr[1] = dev;
5981 }
5982
5983 return 0;
5984}
5985
5986static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
5987 struct genl_info *info)
5988{
5989 if (info->user_ptr[0])
5990 cfg80211_unlock_rdev(info->user_ptr[0]);
5991 if (info->user_ptr[1])
5992 dev_put(info->user_ptr[1]);
5993 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
5994 rtnl_unlock();
5995}
5996
Johannes Berg55682962007-09-20 13:09:35 -04005997static struct genl_ops nl80211_ops[] = {
5998 {
5999 .cmd = NL80211_CMD_GET_WIPHY,
6000 .doit = nl80211_get_wiphy,
6001 .dumpit = nl80211_dump_wiphy,
6002 .policy = nl80211_policy,
6003 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006004 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04006005 },
6006 {
6007 .cmd = NL80211_CMD_SET_WIPHY,
6008 .doit = nl80211_set_wiphy,
6009 .policy = nl80211_policy,
6010 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006011 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006012 },
6013 {
6014 .cmd = NL80211_CMD_GET_INTERFACE,
6015 .doit = nl80211_get_interface,
6016 .dumpit = nl80211_dump_interface,
6017 .policy = nl80211_policy,
6018 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006019 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04006020 },
6021 {
6022 .cmd = NL80211_CMD_SET_INTERFACE,
6023 .doit = nl80211_set_interface,
6024 .policy = nl80211_policy,
6025 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006026 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6027 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006028 },
6029 {
6030 .cmd = NL80211_CMD_NEW_INTERFACE,
6031 .doit = nl80211_new_interface,
6032 .policy = nl80211_policy,
6033 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006034 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6035 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006036 },
6037 {
6038 .cmd = NL80211_CMD_DEL_INTERFACE,
6039 .doit = nl80211_del_interface,
6040 .policy = nl80211_policy,
6041 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006042 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6043 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006044 },
Johannes Berg41ade002007-12-19 02:03:29 +01006045 {
6046 .cmd = NL80211_CMD_GET_KEY,
6047 .doit = nl80211_get_key,
6048 .policy = nl80211_policy,
6049 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006050 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6051 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006052 },
6053 {
6054 .cmd = NL80211_CMD_SET_KEY,
6055 .doit = nl80211_set_key,
6056 .policy = nl80211_policy,
6057 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006058 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006059 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006060 },
6061 {
6062 .cmd = NL80211_CMD_NEW_KEY,
6063 .doit = nl80211_new_key,
6064 .policy = nl80211_policy,
6065 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006066 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006067 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006068 },
6069 {
6070 .cmd = NL80211_CMD_DEL_KEY,
6071 .doit = nl80211_del_key,
6072 .policy = nl80211_policy,
6073 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006074 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006075 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006076 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01006077 {
6078 .cmd = NL80211_CMD_SET_BEACON,
6079 .policy = nl80211_policy,
6080 .flags = GENL_ADMIN_PERM,
6081 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02006082 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6083 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006084 },
6085 {
6086 .cmd = NL80211_CMD_NEW_BEACON,
6087 .policy = nl80211_policy,
6088 .flags = GENL_ADMIN_PERM,
6089 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02006090 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6091 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006092 },
6093 {
6094 .cmd = NL80211_CMD_DEL_BEACON,
6095 .policy = nl80211_policy,
6096 .flags = GENL_ADMIN_PERM,
6097 .doit = nl80211_del_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02006098 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6099 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006100 },
Johannes Berg5727ef12007-12-19 02:03:34 +01006101 {
6102 .cmd = NL80211_CMD_GET_STATION,
6103 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006104 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01006105 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02006106 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6107 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006108 },
6109 {
6110 .cmd = NL80211_CMD_SET_STATION,
6111 .doit = nl80211_set_station,
6112 .policy = nl80211_policy,
6113 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006114 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6115 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006116 },
6117 {
6118 .cmd = NL80211_CMD_NEW_STATION,
6119 .doit = nl80211_new_station,
6120 .policy = nl80211_policy,
6121 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006122 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006123 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006124 },
6125 {
6126 .cmd = NL80211_CMD_DEL_STATION,
6127 .doit = nl80211_del_station,
6128 .policy = nl80211_policy,
6129 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006130 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6131 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006132 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006133 {
6134 .cmd = NL80211_CMD_GET_MPATH,
6135 .doit = nl80211_get_mpath,
6136 .dumpit = nl80211_dump_mpath,
6137 .policy = nl80211_policy,
6138 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006139 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006140 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006141 },
6142 {
6143 .cmd = NL80211_CMD_SET_MPATH,
6144 .doit = nl80211_set_mpath,
6145 .policy = nl80211_policy,
6146 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006147 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006148 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006149 },
6150 {
6151 .cmd = NL80211_CMD_NEW_MPATH,
6152 .doit = nl80211_new_mpath,
6153 .policy = nl80211_policy,
6154 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006155 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006156 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006157 },
6158 {
6159 .cmd = NL80211_CMD_DEL_MPATH,
6160 .doit = nl80211_del_mpath,
6161 .policy = nl80211_policy,
6162 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006163 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6164 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006165 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006166 {
6167 .cmd = NL80211_CMD_SET_BSS,
6168 .doit = nl80211_set_bss,
6169 .policy = nl80211_policy,
6170 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006171 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6172 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006173 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006174 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08006175 .cmd = NL80211_CMD_GET_REG,
6176 .doit = nl80211_get_reg,
6177 .policy = nl80211_policy,
6178 /* can be retrieved by unprivileged users */
6179 },
6180 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006181 .cmd = NL80211_CMD_SET_REG,
6182 .doit = nl80211_set_reg,
6183 .policy = nl80211_policy,
6184 .flags = GENL_ADMIN_PERM,
6185 },
6186 {
6187 .cmd = NL80211_CMD_REQ_SET_REG,
6188 .doit = nl80211_req_set_reg,
6189 .policy = nl80211_policy,
6190 .flags = GENL_ADMIN_PERM,
6191 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006192 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006193 .cmd = NL80211_CMD_GET_MESH_CONFIG,
6194 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006195 .policy = nl80211_policy,
6196 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006197 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6198 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006199 },
6200 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006201 .cmd = NL80211_CMD_SET_MESH_CONFIG,
6202 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006203 .policy = nl80211_policy,
6204 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01006205 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006206 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006207 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02006208 {
Johannes Berg2a519312009-02-10 21:25:55 +01006209 .cmd = NL80211_CMD_TRIGGER_SCAN,
6210 .doit = nl80211_trigger_scan,
6211 .policy = nl80211_policy,
6212 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006213 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006214 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01006215 },
6216 {
6217 .cmd = NL80211_CMD_GET_SCAN,
6218 .policy = nl80211_policy,
6219 .dumpit = nl80211_dump_scan,
6220 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02006221 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03006222 .cmd = NL80211_CMD_START_SCHED_SCAN,
6223 .doit = nl80211_start_sched_scan,
6224 .policy = nl80211_policy,
6225 .flags = GENL_ADMIN_PERM,
6226 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6227 NL80211_FLAG_NEED_RTNL,
6228 },
6229 {
6230 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
6231 .doit = nl80211_stop_sched_scan,
6232 .policy = nl80211_policy,
6233 .flags = GENL_ADMIN_PERM,
6234 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6235 NL80211_FLAG_NEED_RTNL,
6236 },
6237 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02006238 .cmd = NL80211_CMD_AUTHENTICATE,
6239 .doit = nl80211_authenticate,
6240 .policy = nl80211_policy,
6241 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006242 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006243 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006244 },
6245 {
6246 .cmd = NL80211_CMD_ASSOCIATE,
6247 .doit = nl80211_associate,
6248 .policy = nl80211_policy,
6249 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006250 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006251 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006252 },
6253 {
6254 .cmd = NL80211_CMD_DEAUTHENTICATE,
6255 .doit = nl80211_deauthenticate,
6256 .policy = nl80211_policy,
6257 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006258 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006259 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006260 },
6261 {
6262 .cmd = NL80211_CMD_DISASSOCIATE,
6263 .doit = nl80211_disassociate,
6264 .policy = nl80211_policy,
6265 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006266 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006267 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006268 },
Johannes Berg04a773a2009-04-19 21:24:32 +02006269 {
6270 .cmd = NL80211_CMD_JOIN_IBSS,
6271 .doit = nl80211_join_ibss,
6272 .policy = nl80211_policy,
6273 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006274 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006275 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006276 },
6277 {
6278 .cmd = NL80211_CMD_LEAVE_IBSS,
6279 .doit = nl80211_leave_ibss,
6280 .policy = nl80211_policy,
6281 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006282 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006283 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006284 },
Johannes Bergaff89a92009-07-01 21:26:51 +02006285#ifdef CONFIG_NL80211_TESTMODE
6286 {
6287 .cmd = NL80211_CMD_TESTMODE,
6288 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006289 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02006290 .policy = nl80211_policy,
6291 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006292 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6293 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02006294 },
6295#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02006296 {
6297 .cmd = NL80211_CMD_CONNECT,
6298 .doit = nl80211_connect,
6299 .policy = nl80211_policy,
6300 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006301 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006302 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006303 },
6304 {
6305 .cmd = NL80211_CMD_DISCONNECT,
6306 .doit = nl80211_disconnect,
6307 .policy = nl80211_policy,
6308 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006309 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006310 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006311 },
Johannes Berg463d0182009-07-14 00:33:35 +02006312 {
6313 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
6314 .doit = nl80211_wiphy_netns,
6315 .policy = nl80211_policy,
6316 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006317 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6318 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02006319 },
Holger Schurig61fa7132009-11-11 12:25:40 +01006320 {
6321 .cmd = NL80211_CMD_GET_SURVEY,
6322 .policy = nl80211_policy,
6323 .dumpit = nl80211_dump_survey,
6324 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006325 {
6326 .cmd = NL80211_CMD_SET_PMKSA,
6327 .doit = nl80211_setdel_pmksa,
6328 .policy = nl80211_policy,
6329 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006330 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6331 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006332 },
6333 {
6334 .cmd = NL80211_CMD_DEL_PMKSA,
6335 .doit = nl80211_setdel_pmksa,
6336 .policy = nl80211_policy,
6337 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006338 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6339 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006340 },
6341 {
6342 .cmd = NL80211_CMD_FLUSH_PMKSA,
6343 .doit = nl80211_flush_pmksa,
6344 .policy = nl80211_policy,
6345 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006346 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6347 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006348 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006349 {
6350 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
6351 .doit = nl80211_remain_on_channel,
6352 .policy = nl80211_policy,
6353 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006354 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006355 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006356 },
6357 {
6358 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6359 .doit = nl80211_cancel_remain_on_channel,
6360 .policy = nl80211_policy,
6361 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006362 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006363 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006364 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006365 {
6366 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
6367 .doit = nl80211_set_tx_bitrate_mask,
6368 .policy = nl80211_policy,
6369 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006370 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6371 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006372 },
Jouni Malinen026331c2010-02-15 12:53:10 +02006373 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006374 .cmd = NL80211_CMD_REGISTER_FRAME,
6375 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006376 .policy = nl80211_policy,
6377 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006378 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6379 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006380 },
6381 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006382 .cmd = NL80211_CMD_FRAME,
6383 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006384 .policy = nl80211_policy,
6385 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006386 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006387 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006388 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02006389 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006390 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
6391 .doit = nl80211_tx_mgmt_cancel_wait,
6392 .policy = nl80211_policy,
6393 .flags = GENL_ADMIN_PERM,
6394 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6395 NL80211_FLAG_NEED_RTNL,
6396 },
6397 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02006398 .cmd = NL80211_CMD_SET_POWER_SAVE,
6399 .doit = nl80211_set_power_save,
6400 .policy = nl80211_policy,
6401 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006402 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6403 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006404 },
6405 {
6406 .cmd = NL80211_CMD_GET_POWER_SAVE,
6407 .doit = nl80211_get_power_save,
6408 .policy = nl80211_policy,
6409 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006410 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6411 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006412 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006413 {
6414 .cmd = NL80211_CMD_SET_CQM,
6415 .doit = nl80211_set_cqm,
6416 .policy = nl80211_policy,
6417 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006418 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6419 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006420 },
Johannes Bergf444de02010-05-05 15:25:02 +02006421 {
6422 .cmd = NL80211_CMD_SET_CHANNEL,
6423 .doit = nl80211_set_channel,
6424 .policy = nl80211_policy,
6425 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006426 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6427 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02006428 },
Bill Jordane8347eb2010-10-01 13:54:28 -04006429 {
6430 .cmd = NL80211_CMD_SET_WDS_PEER,
6431 .doit = nl80211_set_wds_peer,
6432 .policy = nl80211_policy,
6433 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02006434 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6435 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04006436 },
Johannes Berg29cbe682010-12-03 09:20:44 +01006437 {
6438 .cmd = NL80211_CMD_JOIN_MESH,
6439 .doit = nl80211_join_mesh,
6440 .policy = nl80211_policy,
6441 .flags = GENL_ADMIN_PERM,
6442 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6443 NL80211_FLAG_NEED_RTNL,
6444 },
6445 {
6446 .cmd = NL80211_CMD_LEAVE_MESH,
6447 .doit = nl80211_leave_mesh,
6448 .policy = nl80211_policy,
6449 .flags = GENL_ADMIN_PERM,
6450 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6451 NL80211_FLAG_NEED_RTNL,
6452 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02006453 {
6454 .cmd = NL80211_CMD_GET_WOWLAN,
6455 .doit = nl80211_get_wowlan,
6456 .policy = nl80211_policy,
6457 /* can be retrieved by unprivileged users */
6458 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6459 NL80211_FLAG_NEED_RTNL,
6460 },
6461 {
6462 .cmd = NL80211_CMD_SET_WOWLAN,
6463 .doit = nl80211_set_wowlan,
6464 .policy = nl80211_policy,
6465 .flags = GENL_ADMIN_PERM,
6466 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6467 NL80211_FLAG_NEED_RTNL,
6468 },
Johannes Berge5497d72011-07-05 16:35:40 +02006469 {
6470 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
6471 .doit = nl80211_set_rekey_data,
6472 .policy = nl80211_policy,
6473 .flags = GENL_ADMIN_PERM,
6474 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6475 NL80211_FLAG_NEED_RTNL,
6476 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03006477 {
6478 .cmd = NL80211_CMD_TDLS_MGMT,
6479 .doit = nl80211_tdls_mgmt,
6480 .policy = nl80211_policy,
6481 .flags = GENL_ADMIN_PERM,
6482 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6483 NL80211_FLAG_NEED_RTNL,
6484 },
6485 {
6486 .cmd = NL80211_CMD_TDLS_OPER,
6487 .doit = nl80211_tdls_oper,
6488 .policy = nl80211_policy,
6489 .flags = GENL_ADMIN_PERM,
6490 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6491 NL80211_FLAG_NEED_RTNL,
6492 },
Johannes Berg28946da2011-11-04 11:18:12 +01006493 {
6494 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
6495 .doit = nl80211_register_unexpected_frame,
6496 .policy = nl80211_policy,
6497 .flags = GENL_ADMIN_PERM,
6498 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6499 NL80211_FLAG_NEED_RTNL,
6500 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01006501 {
6502 .cmd = NL80211_CMD_PROBE_CLIENT,
6503 .doit = nl80211_probe_client,
6504 .policy = nl80211_policy,
6505 .flags = GENL_ADMIN_PERM,
6506 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6507 NL80211_FLAG_NEED_RTNL,
6508 },
Johannes Berg5e7602302011-11-04 11:18:17 +01006509 {
6510 .cmd = NL80211_CMD_REGISTER_BEACONS,
6511 .doit = nl80211_register_beacons,
6512 .policy = nl80211_policy,
6513 .flags = GENL_ADMIN_PERM,
6514 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6515 NL80211_FLAG_NEED_RTNL,
6516 },
Johannes Berg55682962007-09-20 13:09:35 -04006517};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006518
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006519static struct genl_multicast_group nl80211_mlme_mcgrp = {
6520 .name = "mlme",
6521};
Johannes Berg55682962007-09-20 13:09:35 -04006522
6523/* multicast groups */
6524static struct genl_multicast_group nl80211_config_mcgrp = {
6525 .name = "config",
6526};
Johannes Berg2a519312009-02-10 21:25:55 +01006527static struct genl_multicast_group nl80211_scan_mcgrp = {
6528 .name = "scan",
6529};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006530static struct genl_multicast_group nl80211_regulatory_mcgrp = {
6531 .name = "regulatory",
6532};
Johannes Berg55682962007-09-20 13:09:35 -04006533
6534/* notification functions */
6535
6536void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
6537{
6538 struct sk_buff *msg;
6539
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006540 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006541 if (!msg)
6542 return;
6543
6544 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
6545 nlmsg_free(msg);
6546 return;
6547 }
6548
Johannes Berg463d0182009-07-14 00:33:35 +02006549 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6550 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006551}
6552
Johannes Berg362a4152009-05-24 16:43:15 +02006553static int nl80211_add_scan_req(struct sk_buff *msg,
6554 struct cfg80211_registered_device *rdev)
6555{
6556 struct cfg80211_scan_request *req = rdev->scan_req;
6557 struct nlattr *nest;
6558 int i;
6559
Johannes Berg667503dd2009-07-07 03:56:11 +02006560 ASSERT_RDEV_LOCK(rdev);
6561
Johannes Berg362a4152009-05-24 16:43:15 +02006562 if (WARN_ON(!req))
6563 return 0;
6564
6565 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
6566 if (!nest)
6567 goto nla_put_failure;
6568 for (i = 0; i < req->n_ssids; i++)
6569 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
6570 nla_nest_end(msg, nest);
6571
6572 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
6573 if (!nest)
6574 goto nla_put_failure;
6575 for (i = 0; i < req->n_channels; i++)
6576 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
6577 nla_nest_end(msg, nest);
6578
6579 if (req->ie)
6580 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
6581
6582 return 0;
6583 nla_put_failure:
6584 return -ENOBUFS;
6585}
6586
Johannes Berga538e2d2009-06-16 19:56:42 +02006587static int nl80211_send_scan_msg(struct sk_buff *msg,
6588 struct cfg80211_registered_device *rdev,
6589 struct net_device *netdev,
6590 u32 pid, u32 seq, int flags,
6591 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01006592{
6593 void *hdr;
6594
6595 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6596 if (!hdr)
6597 return -1;
6598
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05006599 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01006600 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6601
Johannes Berg362a4152009-05-24 16:43:15 +02006602 /* ignore errors and send incomplete event anyway */
6603 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01006604
6605 return genlmsg_end(msg, hdr);
6606
6607 nla_put_failure:
6608 genlmsg_cancel(msg, hdr);
6609 return -EMSGSIZE;
6610}
6611
Luciano Coelho807f8a82011-05-11 17:09:35 +03006612static int
6613nl80211_send_sched_scan_msg(struct sk_buff *msg,
6614 struct cfg80211_registered_device *rdev,
6615 struct net_device *netdev,
6616 u32 pid, u32 seq, int flags, u32 cmd)
6617{
6618 void *hdr;
6619
6620 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6621 if (!hdr)
6622 return -1;
6623
6624 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6625 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6626
6627 return genlmsg_end(msg, hdr);
6628
6629 nla_put_failure:
6630 genlmsg_cancel(msg, hdr);
6631 return -EMSGSIZE;
6632}
6633
Johannes Berga538e2d2009-06-16 19:56:42 +02006634void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
6635 struct net_device *netdev)
6636{
6637 struct sk_buff *msg;
6638
6639 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6640 if (!msg)
6641 return;
6642
6643 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6644 NL80211_CMD_TRIGGER_SCAN) < 0) {
6645 nlmsg_free(msg);
6646 return;
6647 }
6648
Johannes Berg463d0182009-07-14 00:33:35 +02006649 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6650 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02006651}
6652
Johannes Berg2a519312009-02-10 21:25:55 +01006653void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
6654 struct net_device *netdev)
6655{
6656 struct sk_buff *msg;
6657
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006658 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006659 if (!msg)
6660 return;
6661
Johannes Berga538e2d2009-06-16 19:56:42 +02006662 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6663 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006664 nlmsg_free(msg);
6665 return;
6666 }
6667
Johannes Berg463d0182009-07-14 00:33:35 +02006668 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6669 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006670}
6671
6672void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
6673 struct net_device *netdev)
6674{
6675 struct sk_buff *msg;
6676
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006677 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006678 if (!msg)
6679 return;
6680
Johannes Berga538e2d2009-06-16 19:56:42 +02006681 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6682 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006683 nlmsg_free(msg);
6684 return;
6685 }
6686
Johannes Berg463d0182009-07-14 00:33:35 +02006687 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6688 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006689}
6690
Luciano Coelho807f8a82011-05-11 17:09:35 +03006691void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
6692 struct net_device *netdev)
6693{
6694 struct sk_buff *msg;
6695
6696 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6697 if (!msg)
6698 return;
6699
6700 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
6701 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
6702 nlmsg_free(msg);
6703 return;
6704 }
6705
6706 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6707 nl80211_scan_mcgrp.id, GFP_KERNEL);
6708}
6709
6710void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
6711 struct net_device *netdev, u32 cmd)
6712{
6713 struct sk_buff *msg;
6714
6715 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6716 if (!msg)
6717 return;
6718
6719 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
6720 nlmsg_free(msg);
6721 return;
6722 }
6723
6724 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6725 nl80211_scan_mcgrp.id, GFP_KERNEL);
6726}
6727
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006728/*
6729 * This can happen on global regulatory changes or device specific settings
6730 * based on custom world regulatory domains.
6731 */
6732void nl80211_send_reg_change_event(struct regulatory_request *request)
6733{
6734 struct sk_buff *msg;
6735 void *hdr;
6736
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006737 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006738 if (!msg)
6739 return;
6740
6741 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
6742 if (!hdr) {
6743 nlmsg_free(msg);
6744 return;
6745 }
6746
6747 /* Userspace can always count this one always being set */
6748 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
6749
6750 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
6751 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6752 NL80211_REGDOM_TYPE_WORLD);
6753 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
6754 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6755 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
6756 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
6757 request->intersect)
6758 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6759 NL80211_REGDOM_TYPE_INTERSECTION);
6760 else {
6761 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6762 NL80211_REGDOM_TYPE_COUNTRY);
6763 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
6764 }
6765
6766 if (wiphy_idx_valid(request->wiphy_idx))
6767 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
6768
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006769 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006770
Johannes Bergbc43b282009-07-25 10:54:13 +02006771 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02006772 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02006773 GFP_ATOMIC);
6774 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006775
6776 return;
6777
6778nla_put_failure:
6779 genlmsg_cancel(msg, hdr);
6780 nlmsg_free(msg);
6781}
6782
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006783static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
6784 struct net_device *netdev,
6785 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006786 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006787{
6788 struct sk_buff *msg;
6789 void *hdr;
6790
Johannes Berge6d6e342009-07-01 21:26:47 +02006791 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006792 if (!msg)
6793 return;
6794
6795 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6796 if (!hdr) {
6797 nlmsg_free(msg);
6798 return;
6799 }
6800
6801 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6802 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6803 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6804
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006805 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006806
Johannes Berg463d0182009-07-14 00:33:35 +02006807 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6808 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006809 return;
6810
6811 nla_put_failure:
6812 genlmsg_cancel(msg, hdr);
6813 nlmsg_free(msg);
6814}
6815
6816void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006817 struct net_device *netdev, const u8 *buf,
6818 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006819{
6820 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006821 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006822}
6823
6824void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
6825 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006826 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006827{
Johannes Berge6d6e342009-07-01 21:26:47 +02006828 nl80211_send_mlme_event(rdev, netdev, buf, len,
6829 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006830}
6831
Jouni Malinen53b46b82009-03-27 20:53:56 +02006832void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006833 struct net_device *netdev, const u8 *buf,
6834 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006835{
6836 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006837 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006838}
6839
Jouni Malinen53b46b82009-03-27 20:53:56 +02006840void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
6841 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006842 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006843{
6844 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006845 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006846}
6847
Jouni Malinencf4e5942010-12-16 00:52:40 +02006848void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
6849 struct net_device *netdev, const u8 *buf,
6850 size_t len, gfp_t gfp)
6851{
6852 nl80211_send_mlme_event(rdev, netdev, buf, len,
6853 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
6854}
6855
6856void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
6857 struct net_device *netdev, const u8 *buf,
6858 size_t len, gfp_t gfp)
6859{
6860 nl80211_send_mlme_event(rdev, netdev, buf, len,
6861 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
6862}
6863
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04006864static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
6865 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02006866 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006867{
6868 struct sk_buff *msg;
6869 void *hdr;
6870
Johannes Berge6d6e342009-07-01 21:26:47 +02006871 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006872 if (!msg)
6873 return;
6874
6875 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6876 if (!hdr) {
6877 nlmsg_free(msg);
6878 return;
6879 }
6880
6881 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6882 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6883 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
6884 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6885
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006886 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03006887
Johannes Berg463d0182009-07-14 00:33:35 +02006888 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6889 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006890 return;
6891
6892 nla_put_failure:
6893 genlmsg_cancel(msg, hdr);
6894 nlmsg_free(msg);
6895}
6896
6897void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006898 struct net_device *netdev, const u8 *addr,
6899 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006900{
6901 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02006902 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006903}
6904
6905void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006906 struct net_device *netdev, const u8 *addr,
6907 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006908{
Johannes Berge6d6e342009-07-01 21:26:47 +02006909 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
6910 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006911}
6912
Samuel Ortizb23aa672009-07-01 21:26:54 +02006913void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
6914 struct net_device *netdev, const u8 *bssid,
6915 const u8 *req_ie, size_t req_ie_len,
6916 const u8 *resp_ie, size_t resp_ie_len,
6917 u16 status, gfp_t gfp)
6918{
6919 struct sk_buff *msg;
6920 void *hdr;
6921
6922 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6923 if (!msg)
6924 return;
6925
6926 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
6927 if (!hdr) {
6928 nlmsg_free(msg);
6929 return;
6930 }
6931
6932 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6933 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6934 if (bssid)
6935 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6936 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
6937 if (req_ie)
6938 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6939 if (resp_ie)
6940 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6941
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006942 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006943
Johannes Berg463d0182009-07-14 00:33:35 +02006944 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6945 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006946 return;
6947
6948 nla_put_failure:
6949 genlmsg_cancel(msg, hdr);
6950 nlmsg_free(msg);
6951
6952}
6953
6954void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
6955 struct net_device *netdev, const u8 *bssid,
6956 const u8 *req_ie, size_t req_ie_len,
6957 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
6958{
6959 struct sk_buff *msg;
6960 void *hdr;
6961
6962 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6963 if (!msg)
6964 return;
6965
6966 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
6967 if (!hdr) {
6968 nlmsg_free(msg);
6969 return;
6970 }
6971
6972 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6973 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6974 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6975 if (req_ie)
6976 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6977 if (resp_ie)
6978 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6979
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006980 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006981
Johannes Berg463d0182009-07-14 00:33:35 +02006982 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6983 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006984 return;
6985
6986 nla_put_failure:
6987 genlmsg_cancel(msg, hdr);
6988 nlmsg_free(msg);
6989
6990}
6991
6992void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
6993 struct net_device *netdev, u16 reason,
Johannes Berg667503dd2009-07-07 03:56:11 +02006994 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02006995{
6996 struct sk_buff *msg;
6997 void *hdr;
6998
Johannes Berg667503dd2009-07-07 03:56:11 +02006999 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007000 if (!msg)
7001 return;
7002
7003 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
7004 if (!hdr) {
7005 nlmsg_free(msg);
7006 return;
7007 }
7008
7009 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7010 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7011 if (from_ap && reason)
7012 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
7013 if (from_ap)
7014 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
7015 if (ie)
7016 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
7017
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007018 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007019
Johannes Berg463d0182009-07-14 00:33:35 +02007020 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7021 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007022 return;
7023
7024 nla_put_failure:
7025 genlmsg_cancel(msg, hdr);
7026 nlmsg_free(msg);
7027
7028}
7029
Johannes Berg04a773a2009-04-19 21:24:32 +02007030void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
7031 struct net_device *netdev, const u8 *bssid,
7032 gfp_t gfp)
7033{
7034 struct sk_buff *msg;
7035 void *hdr;
7036
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007037 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007038 if (!msg)
7039 return;
7040
7041 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
7042 if (!hdr) {
7043 nlmsg_free(msg);
7044 return;
7045 }
7046
7047 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7048 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7049 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7050
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007051 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02007052
Johannes Berg463d0182009-07-14 00:33:35 +02007053 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7054 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007055 return;
7056
7057 nla_put_failure:
7058 genlmsg_cancel(msg, hdr);
7059 nlmsg_free(msg);
7060}
7061
Javier Cardonac93b5e72011-04-07 15:08:34 -07007062void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
7063 struct net_device *netdev,
7064 const u8 *macaddr, const u8* ie, u8 ie_len,
7065 gfp_t gfp)
7066{
7067 struct sk_buff *msg;
7068 void *hdr;
7069
7070 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7071 if (!msg)
7072 return;
7073
7074 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
7075 if (!hdr) {
7076 nlmsg_free(msg);
7077 return;
7078 }
7079
7080 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7081 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7082 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
7083 if (ie_len && ie)
7084 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
7085
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007086 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07007087
7088 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7089 nl80211_mlme_mcgrp.id, gfp);
7090 return;
7091
7092 nla_put_failure:
7093 genlmsg_cancel(msg, hdr);
7094 nlmsg_free(msg);
7095}
7096
Jouni Malinena3b8b052009-03-27 21:59:49 +02007097void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
7098 struct net_device *netdev, const u8 *addr,
7099 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02007100 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02007101{
7102 struct sk_buff *msg;
7103 void *hdr;
7104
Johannes Berge6d6e342009-07-01 21:26:47 +02007105 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007106 if (!msg)
7107 return;
7108
7109 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
7110 if (!hdr) {
7111 nlmsg_free(msg);
7112 return;
7113 }
7114
7115 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7116 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7117 if (addr)
7118 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7119 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03007120 if (key_id != -1)
7121 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007122 if (tsc)
7123 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
7124
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007125 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007126
Johannes Berg463d0182009-07-14 00:33:35 +02007127 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7128 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007129 return;
7130
7131 nla_put_failure:
7132 genlmsg_cancel(msg, hdr);
7133 nlmsg_free(msg);
7134}
7135
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007136void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
7137 struct ieee80211_channel *channel_before,
7138 struct ieee80211_channel *channel_after)
7139{
7140 struct sk_buff *msg;
7141 void *hdr;
7142 struct nlattr *nl_freq;
7143
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007144 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007145 if (!msg)
7146 return;
7147
7148 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
7149 if (!hdr) {
7150 nlmsg_free(msg);
7151 return;
7152 }
7153
7154 /*
7155 * Since we are applying the beacon hint to a wiphy we know its
7156 * wiphy_idx is valid
7157 */
7158 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
7159
7160 /* Before */
7161 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
7162 if (!nl_freq)
7163 goto nla_put_failure;
7164 if (nl80211_msg_put_channel(msg, channel_before))
7165 goto nla_put_failure;
7166 nla_nest_end(msg, nl_freq);
7167
7168 /* After */
7169 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
7170 if (!nl_freq)
7171 goto nla_put_failure;
7172 if (nl80211_msg_put_channel(msg, channel_after))
7173 goto nla_put_failure;
7174 nla_nest_end(msg, nl_freq);
7175
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007176 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007177
Johannes Berg463d0182009-07-14 00:33:35 +02007178 rcu_read_lock();
7179 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
7180 GFP_ATOMIC);
7181 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007182
7183 return;
7184
7185nla_put_failure:
7186 genlmsg_cancel(msg, hdr);
7187 nlmsg_free(msg);
7188}
7189
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007190static void nl80211_send_remain_on_chan_event(
7191 int cmd, struct cfg80211_registered_device *rdev,
7192 struct net_device *netdev, u64 cookie,
7193 struct ieee80211_channel *chan,
7194 enum nl80211_channel_type channel_type,
7195 unsigned int duration, gfp_t gfp)
7196{
7197 struct sk_buff *msg;
7198 void *hdr;
7199
7200 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7201 if (!msg)
7202 return;
7203
7204 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7205 if (!hdr) {
7206 nlmsg_free(msg);
7207 return;
7208 }
7209
7210 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7211 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7212 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
7213 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
7214 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7215
7216 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
7217 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
7218
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007219 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007220
7221 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7222 nl80211_mlme_mcgrp.id, gfp);
7223 return;
7224
7225 nla_put_failure:
7226 genlmsg_cancel(msg, hdr);
7227 nlmsg_free(msg);
7228}
7229
7230void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
7231 struct net_device *netdev, u64 cookie,
7232 struct ieee80211_channel *chan,
7233 enum nl80211_channel_type channel_type,
7234 unsigned int duration, gfp_t gfp)
7235{
7236 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
7237 rdev, netdev, cookie, chan,
7238 channel_type, duration, gfp);
7239}
7240
7241void nl80211_send_remain_on_channel_cancel(
7242 struct cfg80211_registered_device *rdev, struct net_device *netdev,
7243 u64 cookie, struct ieee80211_channel *chan,
7244 enum nl80211_channel_type channel_type, gfp_t gfp)
7245{
7246 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7247 rdev, netdev, cookie, chan,
7248 channel_type, 0, gfp);
7249}
7250
Johannes Berg98b62182009-12-23 13:15:44 +01007251void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
7252 struct net_device *dev, const u8 *mac_addr,
7253 struct station_info *sinfo, gfp_t gfp)
7254{
7255 struct sk_buff *msg;
7256
7257 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7258 if (!msg)
7259 return;
7260
7261 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
7262 nlmsg_free(msg);
7263 return;
7264 }
7265
7266 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7267 nl80211_mlme_mcgrp.id, gfp);
7268}
7269
Jouni Malinenec15e682011-03-23 15:29:52 +02007270void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
7271 struct net_device *dev, const u8 *mac_addr,
7272 gfp_t gfp)
7273{
7274 struct sk_buff *msg;
7275 void *hdr;
7276
7277 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7278 if (!msg)
7279 return;
7280
7281 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
7282 if (!hdr) {
7283 nlmsg_free(msg);
7284 return;
7285 }
7286
7287 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7288 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
7289
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007290 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02007291
7292 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7293 nl80211_mlme_mcgrp.id, gfp);
7294 return;
7295
7296 nla_put_failure:
7297 genlmsg_cancel(msg, hdr);
7298 nlmsg_free(msg);
7299}
7300
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007301static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
7302 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01007303{
7304 struct wireless_dev *wdev = dev->ieee80211_ptr;
7305 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7306 struct sk_buff *msg;
7307 void *hdr;
7308 int err;
7309 u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
7310
7311 if (!nlpid)
7312 return false;
7313
7314 msg = nlmsg_new(100, gfp);
7315 if (!msg)
7316 return true;
7317
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007318 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01007319 if (!hdr) {
7320 nlmsg_free(msg);
7321 return true;
7322 }
7323
7324 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7325 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7326 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7327
7328 err = genlmsg_end(msg, hdr);
7329 if (err < 0) {
7330 nlmsg_free(msg);
7331 return true;
7332 }
7333
7334 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7335 return true;
7336
7337 nla_put_failure:
7338 genlmsg_cancel(msg, hdr);
7339 nlmsg_free(msg);
7340 return true;
7341}
7342
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007343bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
7344{
7345 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
7346 addr, gfp);
7347}
7348
7349bool nl80211_unexpected_4addr_frame(struct net_device *dev,
7350 const u8 *addr, gfp_t gfp)
7351{
7352 return __nl80211_unexpected_frame(dev,
7353 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
7354 addr, gfp);
7355}
7356
Johannes Berg2e161f72010-08-12 15:38:38 +02007357int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
7358 struct net_device *netdev, u32 nlpid,
7359 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007360{
7361 struct sk_buff *msg;
7362 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02007363
7364 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7365 if (!msg)
7366 return -ENOMEM;
7367
Johannes Berg2e161f72010-08-12 15:38:38 +02007368 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02007369 if (!hdr) {
7370 nlmsg_free(msg);
7371 return -ENOMEM;
7372 }
7373
7374 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7375 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7376 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
7377 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7378
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007379 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007380
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007381 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
Jouni Malinen026331c2010-02-15 12:53:10 +02007382
7383 nla_put_failure:
7384 genlmsg_cancel(msg, hdr);
7385 nlmsg_free(msg);
7386 return -ENOBUFS;
7387}
7388
Johannes Berg2e161f72010-08-12 15:38:38 +02007389void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
7390 struct net_device *netdev, u64 cookie,
7391 const u8 *buf, size_t len, bool ack,
7392 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007393{
7394 struct sk_buff *msg;
7395 void *hdr;
7396
7397 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7398 if (!msg)
7399 return;
7400
Johannes Berg2e161f72010-08-12 15:38:38 +02007401 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02007402 if (!hdr) {
7403 nlmsg_free(msg);
7404 return;
7405 }
7406
7407 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7408 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7409 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7410 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7411 if (ack)
7412 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7413
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007414 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007415
7416 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
7417 return;
7418
7419 nla_put_failure:
7420 genlmsg_cancel(msg, hdr);
7421 nlmsg_free(msg);
7422}
7423
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007424void
7425nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
7426 struct net_device *netdev,
7427 enum nl80211_cqm_rssi_threshold_event rssi_event,
7428 gfp_t gfp)
7429{
7430 struct sk_buff *msg;
7431 struct nlattr *pinfoattr;
7432 void *hdr;
7433
7434 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7435 if (!msg)
7436 return;
7437
7438 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7439 if (!hdr) {
7440 nlmsg_free(msg);
7441 return;
7442 }
7443
7444 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7445 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7446
7447 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7448 if (!pinfoattr)
7449 goto nla_put_failure;
7450
7451 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
7452 rssi_event);
7453
7454 nla_nest_end(msg, pinfoattr);
7455
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007456 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007457
7458 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7459 nl80211_mlme_mcgrp.id, gfp);
7460 return;
7461
7462 nla_put_failure:
7463 genlmsg_cancel(msg, hdr);
7464 nlmsg_free(msg);
7465}
7466
Johannes Berge5497d72011-07-05 16:35:40 +02007467void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
7468 struct net_device *netdev, const u8 *bssid,
7469 const u8 *replay_ctr, gfp_t gfp)
7470{
7471 struct sk_buff *msg;
7472 struct nlattr *rekey_attr;
7473 void *hdr;
7474
7475 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7476 if (!msg)
7477 return;
7478
7479 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
7480 if (!hdr) {
7481 nlmsg_free(msg);
7482 return;
7483 }
7484
7485 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7486 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7487 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7488
7489 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
7490 if (!rekey_attr)
7491 goto nla_put_failure;
7492
7493 NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
7494 NL80211_REPLAY_CTR_LEN, replay_ctr);
7495
7496 nla_nest_end(msg, rekey_attr);
7497
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007498 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02007499
7500 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7501 nl80211_mlme_mcgrp.id, gfp);
7502 return;
7503
7504 nla_put_failure:
7505 genlmsg_cancel(msg, hdr);
7506 nlmsg_free(msg);
7507}
7508
Jouni Malinenc9df56b2011-09-16 18:56:23 +03007509void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
7510 struct net_device *netdev, int index,
7511 const u8 *bssid, bool preauth, gfp_t gfp)
7512{
7513 struct sk_buff *msg;
7514 struct nlattr *attr;
7515 void *hdr;
7516
7517 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7518 if (!msg)
7519 return;
7520
7521 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
7522 if (!hdr) {
7523 nlmsg_free(msg);
7524 return;
7525 }
7526
7527 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7528 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7529
7530 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
7531 if (!attr)
7532 goto nla_put_failure;
7533
7534 NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index);
7535 NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid);
7536 if (preauth)
7537 NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH);
7538
7539 nla_nest_end(msg, attr);
7540
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007541 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03007542
7543 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7544 nl80211_mlme_mcgrp.id, gfp);
7545 return;
7546
7547 nla_put_failure:
7548 genlmsg_cancel(msg, hdr);
7549 nlmsg_free(msg);
7550}
7551
Johannes Bergc063dbf2010-11-24 08:10:05 +01007552void
7553nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
7554 struct net_device *netdev, const u8 *peer,
7555 u32 num_packets, gfp_t gfp)
7556{
7557 struct sk_buff *msg;
7558 struct nlattr *pinfoattr;
7559 void *hdr;
7560
7561 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7562 if (!msg)
7563 return;
7564
7565 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7566 if (!hdr) {
7567 nlmsg_free(msg);
7568 return;
7569 }
7570
7571 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7572 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7573 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
7574
7575 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7576 if (!pinfoattr)
7577 goto nla_put_failure;
7578
7579 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
7580
7581 nla_nest_end(msg, pinfoattr);
7582
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007583 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01007584
7585 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7586 nl80211_mlme_mcgrp.id, gfp);
7587 return;
7588
7589 nla_put_failure:
7590 genlmsg_cancel(msg, hdr);
7591 nlmsg_free(msg);
7592}
7593
Johannes Berg7f6cf312011-11-04 11:18:15 +01007594void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
7595 u64 cookie, bool acked, gfp_t gfp)
7596{
7597 struct wireless_dev *wdev = dev->ieee80211_ptr;
7598 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7599 struct sk_buff *msg;
7600 void *hdr;
7601 int err;
7602
7603 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7604 if (!msg)
7605 return;
7606
7607 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
7608 if (!hdr) {
7609 nlmsg_free(msg);
7610 return;
7611 }
7612
7613 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7614 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7615 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7616 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7617 if (acked)
7618 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7619
7620 err = genlmsg_end(msg, hdr);
7621 if (err < 0) {
7622 nlmsg_free(msg);
7623 return;
7624 }
7625
7626 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7627 nl80211_mlme_mcgrp.id, gfp);
7628 return;
7629
7630 nla_put_failure:
7631 genlmsg_cancel(msg, hdr);
7632 nlmsg_free(msg);
7633}
7634EXPORT_SYMBOL(cfg80211_probe_status);
7635
Johannes Berg5e7602302011-11-04 11:18:17 +01007636void cfg80211_report_obss_beacon(struct wiphy *wiphy,
7637 const u8 *frame, size_t len,
7638 int freq, gfp_t gfp)
7639{
7640 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
7641 struct sk_buff *msg;
7642 void *hdr;
7643 u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
7644
7645 if (!nlpid)
7646 return;
7647
7648 msg = nlmsg_new(len + 100, gfp);
7649 if (!msg)
7650 return;
7651
7652 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
7653 if (!hdr) {
7654 nlmsg_free(msg);
7655 return;
7656 }
7657
7658 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7659 if (freq)
7660 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
7661 NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
7662
7663 genlmsg_end(msg, hdr);
7664
7665 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7666 return;
7667
7668 nla_put_failure:
7669 genlmsg_cancel(msg, hdr);
7670 nlmsg_free(msg);
7671}
7672EXPORT_SYMBOL(cfg80211_report_obss_beacon);
7673
Jouni Malinen026331c2010-02-15 12:53:10 +02007674static int nl80211_netlink_notify(struct notifier_block * nb,
7675 unsigned long state,
7676 void *_notify)
7677{
7678 struct netlink_notify *notify = _notify;
7679 struct cfg80211_registered_device *rdev;
7680 struct wireless_dev *wdev;
7681
7682 if (state != NETLINK_URELEASE)
7683 return NOTIFY_DONE;
7684
7685 rcu_read_lock();
7686
Johannes Berg5e7602302011-11-04 11:18:17 +01007687 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +02007688 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02007689 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Johannes Berg5e7602302011-11-04 11:18:17 +01007690 if (rdev->ap_beacons_nlpid == notify->pid)
7691 rdev->ap_beacons_nlpid = 0;
7692 }
Jouni Malinen026331c2010-02-15 12:53:10 +02007693
7694 rcu_read_unlock();
7695
7696 return NOTIFY_DONE;
7697}
7698
7699static struct notifier_block nl80211_netlink_notifier = {
7700 .notifier_call = nl80211_netlink_notify,
7701};
7702
Johannes Berg55682962007-09-20 13:09:35 -04007703/* initialisation/exit functions */
7704
7705int nl80211_init(void)
7706{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00007707 int err;
Johannes Berg55682962007-09-20 13:09:35 -04007708
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00007709 err = genl_register_family_with_ops(&nl80211_fam,
7710 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04007711 if (err)
7712 return err;
7713
Johannes Berg55682962007-09-20 13:09:35 -04007714 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
7715 if (err)
7716 goto err_out;
7717
Johannes Berg2a519312009-02-10 21:25:55 +01007718 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
7719 if (err)
7720 goto err_out;
7721
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007722 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
7723 if (err)
7724 goto err_out;
7725
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007726 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
7727 if (err)
7728 goto err_out;
7729
Johannes Bergaff89a92009-07-01 21:26:51 +02007730#ifdef CONFIG_NL80211_TESTMODE
7731 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
7732 if (err)
7733 goto err_out;
7734#endif
7735
Jouni Malinen026331c2010-02-15 12:53:10 +02007736 err = netlink_register_notifier(&nl80211_netlink_notifier);
7737 if (err)
7738 goto err_out;
7739
Johannes Berg55682962007-09-20 13:09:35 -04007740 return 0;
7741 err_out:
7742 genl_unregister_family(&nl80211_fam);
7743 return err;
7744}
7745
7746void nl80211_exit(void)
7747{
Jouni Malinen026331c2010-02-15 12:53:10 +02007748 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04007749 genl_unregister_family(&nl80211_fam);
7750}