blob: ae8ea3827acdc4414337efda769e5e6f3de2909d [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 },
Arik Nemtsov00f740e2011-11-10 11:28:56 +0200200 [NL80211_ATTR_PROBE_RESP] = { .type = NLA_BINARY,
201 .len = IEEE80211_MAX_DATA_LEN },
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -0700202 [NL80211_ATTR_DFS_REGION] = { .type = NLA_U8 },
Johannes Berg55682962007-09-20 13:09:35 -0400203};
204
Johannes Berge31b8212010-10-05 19:39:30 +0200205/* policy for the key attributes */
Alexey Dobriyanb54452b2010-02-18 08:14:31 +0000206static const struct nla_policy nl80211_key_policy[NL80211_KEY_MAX + 1] = {
Johannes Bergfffd0932009-07-08 14:22:54 +0200207 [NL80211_KEY_DATA] = { .type = NLA_BINARY, .len = WLAN_MAX_KEY_LEN },
Johannes Bergb9454e82009-07-08 13:29:08 +0200208 [NL80211_KEY_IDX] = { .type = NLA_U8 },
209 [NL80211_KEY_CIPHER] = { .type = NLA_U32 },
Jouni Malinen81962262011-11-02 23:36:31 +0200210 [NL80211_KEY_SEQ] = { .type = NLA_BINARY, .len = 16 },
Johannes Bergb9454e82009-07-08 13:29:08 +0200211 [NL80211_KEY_DEFAULT] = { .type = NLA_FLAG },
212 [NL80211_KEY_DEFAULT_MGMT] = { .type = NLA_FLAG },
Johannes Berge31b8212010-10-05 19:39:30 +0200213 [NL80211_KEY_TYPE] = { .type = NLA_U32 },
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100214 [NL80211_KEY_DEFAULT_TYPES] = { .type = NLA_NESTED },
215};
216
217/* policy for the key default flags */
218static const struct nla_policy
219nl80211_key_default_policy[NUM_NL80211_KEY_DEFAULT_TYPES] = {
220 [NL80211_KEY_DEFAULT_TYPE_UNICAST] = { .type = NLA_FLAG },
221 [NL80211_KEY_DEFAULT_TYPE_MULTICAST] = { .type = NLA_FLAG },
Johannes Bergb9454e82009-07-08 13:29:08 +0200222};
223
Johannes Bergff1b6e62011-05-04 15:37:28 +0200224/* policy for WoWLAN attributes */
225static const struct nla_policy
226nl80211_wowlan_policy[NUM_NL80211_WOWLAN_TRIG] = {
227 [NL80211_WOWLAN_TRIG_ANY] = { .type = NLA_FLAG },
228 [NL80211_WOWLAN_TRIG_DISCONNECT] = { .type = NLA_FLAG },
229 [NL80211_WOWLAN_TRIG_MAGIC_PKT] = { .type = NLA_FLAG },
230 [NL80211_WOWLAN_TRIG_PKT_PATTERN] = { .type = NLA_NESTED },
Johannes Berg77dbbb12011-07-13 10:48:55 +0200231 [NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE] = { .type = NLA_FLAG },
232 [NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST] = { .type = NLA_FLAG },
233 [NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE] = { .type = NLA_FLAG },
234 [NL80211_WOWLAN_TRIG_RFKILL_RELEASE] = { .type = NLA_FLAG },
Johannes Bergff1b6e62011-05-04 15:37:28 +0200235};
236
Johannes Berge5497d72011-07-05 16:35:40 +0200237/* policy for GTK rekey offload attributes */
238static const struct nla_policy
239nl80211_rekey_policy[NUM_NL80211_REKEY_DATA] = {
240 [NL80211_REKEY_DATA_KEK] = { .len = NL80211_KEK_LEN },
241 [NL80211_REKEY_DATA_KCK] = { .len = NL80211_KCK_LEN },
242 [NL80211_REKEY_DATA_REPLAY_CTR] = { .len = NL80211_REPLAY_CTR_LEN },
243};
244
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300245static const struct nla_policy
246nl80211_match_policy[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1] = {
247 [NL80211_ATTR_SCHED_SCAN_MATCH_SSID] = { .type = NLA_BINARY,
248 .len = IEEE80211_MAX_SSID_LEN },
249};
250
Holger Schuriga0438972009-11-11 11:30:02 +0100251/* ifidx get helper */
252static int nl80211_get_ifidx(struct netlink_callback *cb)
253{
254 int res;
255
256 res = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
257 nl80211_fam.attrbuf, nl80211_fam.maxattr,
258 nl80211_policy);
259 if (res)
260 return res;
261
262 if (!nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX])
263 return -EINVAL;
264
265 res = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_IFINDEX]);
266 if (!res)
267 return -EINVAL;
268 return res;
269}
270
Johannes Berg67748892010-10-04 21:14:06 +0200271static int nl80211_prepare_netdev_dump(struct sk_buff *skb,
272 struct netlink_callback *cb,
273 struct cfg80211_registered_device **rdev,
274 struct net_device **dev)
275{
276 int ifidx = cb->args[0];
277 int err;
278
279 if (!ifidx)
280 ifidx = nl80211_get_ifidx(cb);
281 if (ifidx < 0)
282 return ifidx;
283
284 cb->args[0] = ifidx;
285
286 rtnl_lock();
287
288 *dev = __dev_get_by_index(sock_net(skb->sk), ifidx);
289 if (!*dev) {
290 err = -ENODEV;
291 goto out_rtnl;
292 }
293
294 *rdev = cfg80211_get_dev_from_ifindex(sock_net(skb->sk), ifidx);
Felix Fietkau3cc25e52010-10-31 15:31:54 +0100295 if (IS_ERR(*rdev)) {
296 err = PTR_ERR(*rdev);
Johannes Berg67748892010-10-04 21:14:06 +0200297 goto out_rtnl;
298 }
299
300 return 0;
301 out_rtnl:
302 rtnl_unlock();
303 return err;
304}
305
306static void nl80211_finish_netdev_dump(struct cfg80211_registered_device *rdev)
307{
308 cfg80211_unlock_rdev(rdev);
309 rtnl_unlock();
310}
311
Johannes Bergf4a11bb2009-03-27 12:40:28 +0100312/* IE validation */
313static bool is_valid_ie_attr(const struct nlattr *attr)
314{
315 const u8 *pos;
316 int len;
317
318 if (!attr)
319 return true;
320
321 pos = nla_data(attr);
322 len = nla_len(attr);
323
324 while (len) {
325 u8 elemlen;
326
327 if (len < 2)
328 return false;
329 len -= 2;
330
331 elemlen = pos[1];
332 if (elemlen > len)
333 return false;
334
335 len -= elemlen;
336 pos += 2 + elemlen;
337 }
338
339 return true;
340}
341
Johannes Berg55682962007-09-20 13:09:35 -0400342/* message building helper */
343static inline void *nl80211hdr_put(struct sk_buff *skb, u32 pid, u32 seq,
344 int flags, u8 cmd)
345{
346 /* since there is no private header just add the generic one */
347 return genlmsg_put(skb, pid, seq, &nl80211_fam, flags, cmd);
348}
349
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400350static int nl80211_msg_put_channel(struct sk_buff *msg,
351 struct ieee80211_channel *chan)
352{
353 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_FREQ,
354 chan->center_freq);
355
356 if (chan->flags & IEEE80211_CHAN_DISABLED)
357 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_DISABLED);
358 if (chan->flags & IEEE80211_CHAN_PASSIVE_SCAN)
359 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_PASSIVE_SCAN);
360 if (chan->flags & IEEE80211_CHAN_NO_IBSS)
361 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_NO_IBSS);
362 if (chan->flags & IEEE80211_CHAN_RADAR)
363 NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR);
364
365 NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER,
366 DBM_TO_MBM(chan->max_power));
367
368 return 0;
369
370 nla_put_failure:
371 return -ENOBUFS;
372}
373
Johannes Berg55682962007-09-20 13:09:35 -0400374/* netlink command implementations */
375
Johannes Bergb9454e82009-07-08 13:29:08 +0200376struct key_parse {
377 struct key_params p;
378 int idx;
Johannes Berge31b8212010-10-05 19:39:30 +0200379 int type;
Johannes Bergb9454e82009-07-08 13:29:08 +0200380 bool def, defmgmt;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100381 bool def_uni, def_multi;
Johannes Bergb9454e82009-07-08 13:29:08 +0200382};
383
384static int nl80211_parse_key_new(struct nlattr *key, struct key_parse *k)
385{
386 struct nlattr *tb[NL80211_KEY_MAX + 1];
387 int err = nla_parse_nested(tb, NL80211_KEY_MAX, key,
388 nl80211_key_policy);
389 if (err)
390 return err;
391
392 k->def = !!tb[NL80211_KEY_DEFAULT];
393 k->defmgmt = !!tb[NL80211_KEY_DEFAULT_MGMT];
394
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100395 if (k->def) {
396 k->def_uni = true;
397 k->def_multi = true;
398 }
399 if (k->defmgmt)
400 k->def_multi = true;
401
Johannes Bergb9454e82009-07-08 13:29:08 +0200402 if (tb[NL80211_KEY_IDX])
403 k->idx = nla_get_u8(tb[NL80211_KEY_IDX]);
404
405 if (tb[NL80211_KEY_DATA]) {
406 k->p.key = nla_data(tb[NL80211_KEY_DATA]);
407 k->p.key_len = nla_len(tb[NL80211_KEY_DATA]);
408 }
409
410 if (tb[NL80211_KEY_SEQ]) {
411 k->p.seq = nla_data(tb[NL80211_KEY_SEQ]);
412 k->p.seq_len = nla_len(tb[NL80211_KEY_SEQ]);
413 }
414
415 if (tb[NL80211_KEY_CIPHER])
416 k->p.cipher = nla_get_u32(tb[NL80211_KEY_CIPHER]);
417
Johannes Berge31b8212010-10-05 19:39:30 +0200418 if (tb[NL80211_KEY_TYPE]) {
419 k->type = nla_get_u32(tb[NL80211_KEY_TYPE]);
420 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
421 return -EINVAL;
422 }
423
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100424 if (tb[NL80211_KEY_DEFAULT_TYPES]) {
425 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
426 int err = nla_parse_nested(kdt,
427 NUM_NL80211_KEY_DEFAULT_TYPES - 1,
428 tb[NL80211_KEY_DEFAULT_TYPES],
429 nl80211_key_default_policy);
430 if (err)
431 return err;
432
433 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
434 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
435 }
436
Johannes Bergb9454e82009-07-08 13:29:08 +0200437 return 0;
438}
439
440static int nl80211_parse_key_old(struct genl_info *info, struct key_parse *k)
441{
442 if (info->attrs[NL80211_ATTR_KEY_DATA]) {
443 k->p.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]);
444 k->p.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]);
445 }
446
447 if (info->attrs[NL80211_ATTR_KEY_SEQ]) {
448 k->p.seq = nla_data(info->attrs[NL80211_ATTR_KEY_SEQ]);
449 k->p.seq_len = nla_len(info->attrs[NL80211_ATTR_KEY_SEQ]);
450 }
451
452 if (info->attrs[NL80211_ATTR_KEY_IDX])
453 k->idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
454
455 if (info->attrs[NL80211_ATTR_KEY_CIPHER])
456 k->p.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]);
457
458 k->def = !!info->attrs[NL80211_ATTR_KEY_DEFAULT];
459 k->defmgmt = !!info->attrs[NL80211_ATTR_KEY_DEFAULT_MGMT];
460
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100461 if (k->def) {
462 k->def_uni = true;
463 k->def_multi = true;
464 }
465 if (k->defmgmt)
466 k->def_multi = true;
467
Johannes Berge31b8212010-10-05 19:39:30 +0200468 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
469 k->type = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
470 if (k->type < 0 || k->type >= NUM_NL80211_KEYTYPES)
471 return -EINVAL;
472 }
473
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100474 if (info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES]) {
475 struct nlattr *kdt[NUM_NL80211_KEY_DEFAULT_TYPES];
476 int err = nla_parse_nested(
477 kdt, NUM_NL80211_KEY_DEFAULT_TYPES - 1,
478 info->attrs[NL80211_ATTR_KEY_DEFAULT_TYPES],
479 nl80211_key_default_policy);
480 if (err)
481 return err;
482
483 k->def_uni = kdt[NL80211_KEY_DEFAULT_TYPE_UNICAST];
484 k->def_multi = kdt[NL80211_KEY_DEFAULT_TYPE_MULTICAST];
485 }
486
Johannes Bergb9454e82009-07-08 13:29:08 +0200487 return 0;
488}
489
490static int nl80211_parse_key(struct genl_info *info, struct key_parse *k)
491{
492 int err;
493
494 memset(k, 0, sizeof(*k));
495 k->idx = -1;
Johannes Berge31b8212010-10-05 19:39:30 +0200496 k->type = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +0200497
498 if (info->attrs[NL80211_ATTR_KEY])
499 err = nl80211_parse_key_new(info->attrs[NL80211_ATTR_KEY], k);
500 else
501 err = nl80211_parse_key_old(info, k);
502
503 if (err)
504 return err;
505
506 if (k->def && k->defmgmt)
507 return -EINVAL;
508
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100509 if (k->defmgmt) {
510 if (k->def_uni || !k->def_multi)
511 return -EINVAL;
512 }
513
Johannes Bergb9454e82009-07-08 13:29:08 +0200514 if (k->idx != -1) {
515 if (k->defmgmt) {
516 if (k->idx < 4 || k->idx > 5)
517 return -EINVAL;
518 } else if (k->def) {
519 if (k->idx < 0 || k->idx > 3)
520 return -EINVAL;
521 } else {
522 if (k->idx < 0 || k->idx > 5)
523 return -EINVAL;
524 }
525 }
526
527 return 0;
528}
529
Johannes Bergfffd0932009-07-08 14:22:54 +0200530static struct cfg80211_cached_keys *
531nl80211_parse_connkeys(struct cfg80211_registered_device *rdev,
532 struct nlattr *keys)
533{
534 struct key_parse parse;
535 struct nlattr *key;
536 struct cfg80211_cached_keys *result;
537 int rem, err, def = 0;
538
539 result = kzalloc(sizeof(*result), GFP_KERNEL);
540 if (!result)
541 return ERR_PTR(-ENOMEM);
542
543 result->def = -1;
544 result->defmgmt = -1;
545
546 nla_for_each_nested(key, keys, rem) {
547 memset(&parse, 0, sizeof(parse));
548 parse.idx = -1;
549
550 err = nl80211_parse_key_new(key, &parse);
551 if (err)
552 goto error;
553 err = -EINVAL;
554 if (!parse.p.key)
555 goto error;
556 if (parse.idx < 0 || parse.idx > 4)
557 goto error;
558 if (parse.def) {
559 if (def)
560 goto error;
561 def = 1;
562 result->def = parse.idx;
Johannes Bergdbd2fd62010-12-09 19:58:59 +0100563 if (!parse.def_uni || !parse.def_multi)
564 goto error;
Johannes Bergfffd0932009-07-08 14:22:54 +0200565 } else if (parse.defmgmt)
566 goto error;
567 err = cfg80211_validate_key_settings(rdev, &parse.p,
Johannes Berge31b8212010-10-05 19:39:30 +0200568 parse.idx, false, NULL);
Johannes Bergfffd0932009-07-08 14:22:54 +0200569 if (err)
570 goto error;
571 result->params[parse.idx].cipher = parse.p.cipher;
572 result->params[parse.idx].key_len = parse.p.key_len;
573 result->params[parse.idx].key = result->data[parse.idx];
574 memcpy(result->data[parse.idx], parse.p.key, parse.p.key_len);
575 }
576
577 return result;
578 error:
579 kfree(result);
580 return ERR_PTR(err);
581}
582
583static int nl80211_key_allowed(struct wireless_dev *wdev)
584{
585 ASSERT_WDEV_LOCK(wdev);
586
Johannes Bergfffd0932009-07-08 14:22:54 +0200587 switch (wdev->iftype) {
588 case NL80211_IFTYPE_AP:
589 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200590 case NL80211_IFTYPE_P2P_GO:
Thomas Pedersenff973af2011-05-03 16:57:12 -0700591 case NL80211_IFTYPE_MESH_POINT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200592 break;
593 case NL80211_IFTYPE_ADHOC:
594 if (!wdev->current_bss)
595 return -ENOLINK;
596 break;
597 case NL80211_IFTYPE_STATION:
Johannes Berg074ac8d2010-09-16 14:58:22 +0200598 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Bergfffd0932009-07-08 14:22:54 +0200599 if (wdev->sme_state != CFG80211_SME_CONNECTED)
600 return -ENOLINK;
601 break;
602 default:
603 return -EINVAL;
604 }
605
606 return 0;
607}
608
Johannes Berg7527a782011-05-13 10:58:57 +0200609static int nl80211_put_iftypes(struct sk_buff *msg, u32 attr, u16 ifmodes)
610{
611 struct nlattr *nl_modes = nla_nest_start(msg, attr);
612 int i;
613
614 if (!nl_modes)
615 goto nla_put_failure;
616
617 i = 0;
618 while (ifmodes) {
619 if (ifmodes & 1)
620 NLA_PUT_FLAG(msg, i);
621 ifmodes >>= 1;
622 i++;
623 }
624
625 nla_nest_end(msg, nl_modes);
626 return 0;
627
628nla_put_failure:
629 return -ENOBUFS;
630}
631
632static int nl80211_put_iface_combinations(struct wiphy *wiphy,
633 struct sk_buff *msg)
634{
635 struct nlattr *nl_combis;
636 int i, j;
637
638 nl_combis = nla_nest_start(msg,
639 NL80211_ATTR_INTERFACE_COMBINATIONS);
640 if (!nl_combis)
641 goto nla_put_failure;
642
643 for (i = 0; i < wiphy->n_iface_combinations; i++) {
644 const struct ieee80211_iface_combination *c;
645 struct nlattr *nl_combi, *nl_limits;
646
647 c = &wiphy->iface_combinations[i];
648
649 nl_combi = nla_nest_start(msg, i + 1);
650 if (!nl_combi)
651 goto nla_put_failure;
652
653 nl_limits = nla_nest_start(msg, NL80211_IFACE_COMB_LIMITS);
654 if (!nl_limits)
655 goto nla_put_failure;
656
657 for (j = 0; j < c->n_limits; j++) {
658 struct nlattr *nl_limit;
659
660 nl_limit = nla_nest_start(msg, j + 1);
661 if (!nl_limit)
662 goto nla_put_failure;
663 NLA_PUT_U32(msg, NL80211_IFACE_LIMIT_MAX,
664 c->limits[j].max);
665 if (nl80211_put_iftypes(msg, NL80211_IFACE_LIMIT_TYPES,
666 c->limits[j].types))
667 goto nla_put_failure;
668 nla_nest_end(msg, nl_limit);
669 }
670
671 nla_nest_end(msg, nl_limits);
672
673 if (c->beacon_int_infra_match)
674 NLA_PUT_FLAG(msg,
675 NL80211_IFACE_COMB_STA_AP_BI_MATCH);
676 NLA_PUT_U32(msg, NL80211_IFACE_COMB_NUM_CHANNELS,
677 c->num_different_channels);
678 NLA_PUT_U32(msg, NL80211_IFACE_COMB_MAXNUM,
679 c->max_interfaces);
680
681 nla_nest_end(msg, nl_combi);
682 }
683
684 nla_nest_end(msg, nl_combis);
685
686 return 0;
687nla_put_failure:
688 return -ENOBUFS;
689}
690
Johannes Berg55682962007-09-20 13:09:35 -0400691static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags,
692 struct cfg80211_registered_device *dev)
693{
694 void *hdr;
Johannes Bergee688b002008-01-24 19:38:39 +0100695 struct nlattr *nl_bands, *nl_band;
696 struct nlattr *nl_freqs, *nl_freq;
697 struct nlattr *nl_rates, *nl_rate;
Johannes Berg8fdc6212009-03-14 09:34:01 +0100698 struct nlattr *nl_cmds;
Johannes Bergee688b002008-01-24 19:38:39 +0100699 enum ieee80211_band band;
700 struct ieee80211_channel *chan;
701 struct ieee80211_rate *rate;
702 int i;
Johannes Berg2e161f72010-08-12 15:38:38 +0200703 const struct ieee80211_txrx_stypes *mgmt_stypes =
704 dev->wiphy.mgmt_stypes;
Johannes Berg55682962007-09-20 13:09:35 -0400705
706 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY);
707 if (!hdr)
708 return -1;
709
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -0500710 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -0400711 NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy));
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200712
Johannes Bergf5ea9122009-08-07 16:17:38 +0200713 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
714 cfg80211_rdev_list_generation);
715
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +0200716 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_SHORT,
717 dev->wiphy.retry_short);
718 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_RETRY_LONG,
719 dev->wiphy.retry_long);
720 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FRAG_THRESHOLD,
721 dev->wiphy.frag_threshold);
722 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_RTS_THRESHOLD,
723 dev->wiphy.rts_threshold);
Lukáš Turek81077e82009-12-21 22:50:47 +0100724 NLA_PUT_U8(msg, NL80211_ATTR_WIPHY_COVERAGE_CLASS,
725 dev->wiphy.coverage_class);
Johannes Berg2a519312009-02-10 21:25:55 +0100726 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCAN_SSIDS,
727 dev->wiphy.max_scan_ssids);
Luciano Coelho93b6aa62011-07-13 14:57:28 +0300728 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_SCHED_SCAN_SSIDS,
729 dev->wiphy.max_sched_scan_ssids);
Johannes Berg18a83652009-03-31 12:12:05 +0200730 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCAN_IE_LEN,
731 dev->wiphy.max_scan_ie_len);
Luciano Coelho5a865ba2011-07-13 14:57:29 +0300732 NLA_PUT_U16(msg, NL80211_ATTR_MAX_SCHED_SCAN_IE_LEN,
733 dev->wiphy.max_sched_scan_ie_len);
Luciano Coelhoa1f1c212011-08-31 16:01:48 +0300734 NLA_PUT_U8(msg, NL80211_ATTR_MAX_MATCH_SETS,
735 dev->wiphy.max_match_sets);
Johannes Bergee688b002008-01-24 19:38:39 +0100736
Johannes Berge31b8212010-10-05 19:39:30 +0200737 if (dev->wiphy.flags & WIPHY_FLAG_IBSS_RSN)
738 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_IBSS_RSN);
Javier Cardona15d5dda2011-04-07 15:08:28 -0700739 if (dev->wiphy.flags & WIPHY_FLAG_MESH_AUTH)
740 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_MESH_AUTH);
Eliad Pellercedb5412011-08-31 11:29:43 +0300741 if (dev->wiphy.flags & WIPHY_FLAG_AP_UAPSD)
742 NLA_PUT_FLAG(msg, NL80211_ATTR_SUPPORT_AP_UAPSD);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530743 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_FW_ROAM)
744 NLA_PUT_FLAG(msg, NL80211_ATTR_ROAM_SUPPORT);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300745 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS)
746 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_SUPPORT);
747 if (dev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)
748 NLA_PUT_FLAG(msg, NL80211_ATTR_TDLS_EXTERNAL_SETUP);
Vivek Natarajanf4b34b52011-08-29 14:23:03 +0530749
Johannes Berg25e47c12009-04-02 20:14:06 +0200750 NLA_PUT(msg, NL80211_ATTR_CIPHER_SUITES,
751 sizeof(u32) * dev->wiphy.n_cipher_suites,
752 dev->wiphy.cipher_suites);
753
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100754 NLA_PUT_U8(msg, NL80211_ATTR_MAX_NUM_PMKIDS,
755 dev->wiphy.max_num_pmkids);
756
Johannes Bergc0692b82010-08-27 14:26:53 +0300757 if (dev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL)
758 NLA_PUT_FLAG(msg, NL80211_ATTR_CONTROL_PORT_ETHERTYPE);
759
Bruno Randolf39fd5de2010-12-16 11:30:28 +0900760 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_TX,
761 dev->wiphy.available_antennas_tx);
762 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_AVAIL_RX,
763 dev->wiphy.available_antennas_rx);
764
Arik Nemtsov87bbbe22011-11-10 11:28:55 +0200765 if (dev->wiphy.flags & WIPHY_FLAG_AP_PROBE_RESP_OFFLOAD)
766 NLA_PUT_U32(msg, NL80211_ATTR_PROBE_RESP_OFFLOAD,
767 dev->wiphy.probe_resp_offload);
768
Bruno Randolf7f531e02010-12-16 11:30:22 +0900769 if ((dev->wiphy.available_antennas_tx ||
770 dev->wiphy.available_antennas_rx) && dev->ops->get_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +0900771 u32 tx_ant = 0, rx_ant = 0;
772 int res;
773 res = dev->ops->get_antenna(&dev->wiphy, &tx_ant, &rx_ant);
774 if (!res) {
775 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_TX, tx_ant);
776 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_ANTENNA_RX, rx_ant);
777 }
778 }
779
Johannes Berg7527a782011-05-13 10:58:57 +0200780 if (nl80211_put_iftypes(msg, NL80211_ATTR_SUPPORTED_IFTYPES,
781 dev->wiphy.interface_modes))
Luis R. Rodriguezf59ac042008-08-29 16:26:43 -0700782 goto nla_put_failure;
783
Johannes Bergee688b002008-01-24 19:38:39 +0100784 nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS);
785 if (!nl_bands)
786 goto nla_put_failure;
787
788 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
789 if (!dev->wiphy.bands[band])
790 continue;
791
792 nl_band = nla_nest_start(msg, band);
793 if (!nl_band)
794 goto nla_put_failure;
795
Johannes Bergd51626d2008-10-09 12:20:13 +0200796 /* add HT info */
797 if (dev->wiphy.bands[band]->ht_cap.ht_supported) {
798 NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET,
799 sizeof(dev->wiphy.bands[band]->ht_cap.mcs),
800 &dev->wiphy.bands[band]->ht_cap.mcs);
801 NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA,
802 dev->wiphy.bands[band]->ht_cap.cap);
803 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR,
804 dev->wiphy.bands[band]->ht_cap.ampdu_factor);
805 NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY,
806 dev->wiphy.bands[band]->ht_cap.ampdu_density);
807 }
808
Johannes Bergee688b002008-01-24 19:38:39 +0100809 /* add frequencies */
810 nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS);
811 if (!nl_freqs)
812 goto nla_put_failure;
813
814 for (i = 0; i < dev->wiphy.bands[band]->n_channels; i++) {
815 nl_freq = nla_nest_start(msg, i);
816 if (!nl_freq)
817 goto nla_put_failure;
818
819 chan = &dev->wiphy.bands[band]->channels[i];
Johannes Bergee688b002008-01-24 19:38:39 +0100820
Luis R. Rodriguez5dab3b82009-04-02 14:08:08 -0400821 if (nl80211_msg_put_channel(msg, chan))
822 goto nla_put_failure;
Jouni Malinene2f367f262008-11-21 19:01:30 +0200823
Johannes Bergee688b002008-01-24 19:38:39 +0100824 nla_nest_end(msg, nl_freq);
825 }
826
827 nla_nest_end(msg, nl_freqs);
828
829 /* add bitrates */
830 nl_rates = nla_nest_start(msg, NL80211_BAND_ATTR_RATES);
831 if (!nl_rates)
832 goto nla_put_failure;
833
834 for (i = 0; i < dev->wiphy.bands[band]->n_bitrates; i++) {
835 nl_rate = nla_nest_start(msg, i);
836 if (!nl_rate)
837 goto nla_put_failure;
838
839 rate = &dev->wiphy.bands[band]->bitrates[i];
840 NLA_PUT_U32(msg, NL80211_BITRATE_ATTR_RATE,
841 rate->bitrate);
842 if (rate->flags & IEEE80211_RATE_SHORT_PREAMBLE)
843 NLA_PUT_FLAG(msg,
844 NL80211_BITRATE_ATTR_2GHZ_SHORTPREAMBLE);
845
846 nla_nest_end(msg, nl_rate);
847 }
848
849 nla_nest_end(msg, nl_rates);
850
851 nla_nest_end(msg, nl_band);
852 }
853 nla_nest_end(msg, nl_bands);
854
Johannes Berg8fdc6212009-03-14 09:34:01 +0100855 nl_cmds = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_COMMANDS);
856 if (!nl_cmds)
857 goto nla_put_failure;
858
859 i = 0;
860#define CMD(op, n) \
861 do { \
862 if (dev->ops->op) { \
863 i++; \
864 NLA_PUT_U32(msg, i, NL80211_CMD_ ## n); \
865 } \
866 } while (0)
867
868 CMD(add_virtual_intf, NEW_INTERFACE);
869 CMD(change_virtual_intf, SET_INTERFACE);
870 CMD(add_key, NEW_KEY);
871 CMD(add_beacon, NEW_BEACON);
872 CMD(add_station, NEW_STATION);
873 CMD(add_mpath, NEW_MPATH);
Javier Cardona24bdd9f2010-12-16 17:37:48 -0800874 CMD(update_mesh_config, SET_MESH_CONFIG);
Johannes Berg8fdc6212009-03-14 09:34:01 +0100875 CMD(change_bss, SET_BSS);
Jouni Malinen636a5d32009-03-19 13:39:22 +0200876 CMD(auth, AUTHENTICATE);
877 CMD(assoc, ASSOCIATE);
878 CMD(deauth, DEAUTHENTICATE);
879 CMD(disassoc, DISASSOCIATE);
Johannes Berg04a773a2009-04-19 21:24:32 +0200880 CMD(join_ibss, JOIN_IBSS);
Johannes Berg29cbe682010-12-03 09:20:44 +0100881 CMD(join_mesh, JOIN_MESH);
Samuel Ortiz67fbb162009-11-24 23:59:15 +0100882 CMD(set_pmksa, SET_PMKSA);
883 CMD(del_pmksa, DEL_PMKSA);
884 CMD(flush_pmksa, FLUSH_PMKSA);
Jouni Malinen9588bbd2009-12-23 13:15:41 +0100885 CMD(remain_on_channel, REMAIN_ON_CHANNEL);
Jouni Malinen13ae75b2009-12-29 12:59:45 +0200886 CMD(set_bitrate_mask, SET_TX_BITRATE_MASK);
Johannes Berg2e161f72010-08-12 15:38:38 +0200887 CMD(mgmt_tx, FRAME);
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100888 CMD(mgmt_tx_cancel_wait, FRAME_WAIT_CANCEL);
Johannes Berg5be83de2009-11-19 00:56:28 +0100889 if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) {
Johannes Berg463d0182009-07-14 00:33:35 +0200890 i++;
891 NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS);
892 }
Johannes Bergf444de02010-05-05 15:25:02 +0200893 CMD(set_channel, SET_CHANNEL);
Bill Jordane8347eb2010-10-01 13:54:28 -0400894 CMD(set_wds_peer, SET_WDS_PEER);
Arik Nemtsov109086c2011-09-28 14:12:50 +0300895 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) {
896 CMD(tdls_mgmt, TDLS_MGMT);
897 CMD(tdls_oper, TDLS_OPER);
898 }
Luciano Coelho807f8a82011-05-11 17:09:35 +0300899 if (dev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN)
900 CMD(sched_scan_start, START_SCHED_SCAN);
Johannes Berg7f6cf312011-11-04 11:18:15 +0100901 CMD(probe_client, PROBE_CLIENT);
Johannes Berg5e7602302011-11-04 11:18:17 +0100902 if (dev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS) {
903 i++;
904 NLA_PUT_U32(msg, i, NL80211_CMD_REGISTER_BEACONS);
905 }
Johannes Berg8fdc6212009-03-14 09:34:01 +0100906
Kalle Valo4745fc02011-11-17 19:06:10 +0200907#ifdef CONFIG_NL80211_TESTMODE
908 CMD(testmode_cmd, TESTMODE);
909#endif
910
Johannes Berg8fdc6212009-03-14 09:34:01 +0100911#undef CMD
Samuel Ortizb23aa672009-07-01 21:26:54 +0200912
Johannes Berg6829c872009-07-02 09:13:27 +0200913 if (dev->ops->connect || dev->ops->auth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200914 i++;
915 NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT);
916 }
917
Johannes Berg6829c872009-07-02 09:13:27 +0200918 if (dev->ops->disconnect || dev->ops->deauth) {
Samuel Ortizb23aa672009-07-01 21:26:54 +0200919 i++;
920 NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT);
921 }
922
Johannes Berg8fdc6212009-03-14 09:34:01 +0100923 nla_nest_end(msg, nl_cmds);
924
Johannes Berga2939112010-12-14 17:54:28 +0100925 if (dev->ops->remain_on_channel)
926 NLA_PUT_U32(msg, NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
927 dev->wiphy.max_remain_on_channel_duration);
928
Jouni Malinend2da5872011-08-08 12:10:30 +0300929 if (dev->ops->mgmt_tx_cancel_wait)
Johannes Bergf7ca38d2010-11-25 10:02:29 +0100930 NLA_PUT_FLAG(msg, NL80211_ATTR_OFFCHANNEL_TX_OK);
931
Johannes Berg2e161f72010-08-12 15:38:38 +0200932 if (mgmt_stypes) {
933 u16 stypes;
934 struct nlattr *nl_ftypes, *nl_ifs;
935 enum nl80211_iftype ift;
936
937 nl_ifs = nla_nest_start(msg, NL80211_ATTR_TX_FRAME_TYPES);
938 if (!nl_ifs)
939 goto nla_put_failure;
940
941 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
942 nl_ftypes = nla_nest_start(msg, ift);
943 if (!nl_ftypes)
944 goto nla_put_failure;
945 i = 0;
946 stypes = mgmt_stypes[ift].tx;
947 while (stypes) {
948 if (stypes & 1)
949 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
950 (i << 4) | IEEE80211_FTYPE_MGMT);
951 stypes >>= 1;
952 i++;
953 }
954 nla_nest_end(msg, nl_ftypes);
955 }
956
Johannes Berg74b70a42010-08-24 12:15:53 +0200957 nla_nest_end(msg, nl_ifs);
958
Johannes Berg2e161f72010-08-12 15:38:38 +0200959 nl_ifs = nla_nest_start(msg, NL80211_ATTR_RX_FRAME_TYPES);
960 if (!nl_ifs)
961 goto nla_put_failure;
962
963 for (ift = 0; ift < NUM_NL80211_IFTYPES; ift++) {
964 nl_ftypes = nla_nest_start(msg, ift);
965 if (!nl_ftypes)
966 goto nla_put_failure;
967 i = 0;
968 stypes = mgmt_stypes[ift].rx;
969 while (stypes) {
970 if (stypes & 1)
971 NLA_PUT_U16(msg, NL80211_ATTR_FRAME_TYPE,
972 (i << 4) | IEEE80211_FTYPE_MGMT);
973 stypes >>= 1;
974 i++;
975 }
976 nla_nest_end(msg, nl_ftypes);
977 }
978 nla_nest_end(msg, nl_ifs);
979 }
980
Johannes Bergff1b6e62011-05-04 15:37:28 +0200981 if (dev->wiphy.wowlan.flags || dev->wiphy.wowlan.n_patterns) {
982 struct nlattr *nl_wowlan;
983
984 nl_wowlan = nla_nest_start(msg,
985 NL80211_ATTR_WOWLAN_TRIGGERS_SUPPORTED);
986 if (!nl_wowlan)
987 goto nla_put_failure;
988
989 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_ANY)
990 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
991 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_DISCONNECT)
992 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
993 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_MAGIC_PKT)
994 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +0200995 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_SUPPORTS_GTK_REKEY)
996 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED);
997 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE)
998 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
999 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ)
1000 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
1001 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_4WAY_HANDSHAKE)
1002 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
1003 if (dev->wiphy.wowlan.flags & WIPHY_WOWLAN_RFKILL_RELEASE)
1004 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02001005 if (dev->wiphy.wowlan.n_patterns) {
1006 struct nl80211_wowlan_pattern_support pat = {
1007 .max_patterns = dev->wiphy.wowlan.n_patterns,
1008 .min_pattern_len =
1009 dev->wiphy.wowlan.pattern_min_len,
1010 .max_pattern_len =
1011 dev->wiphy.wowlan.pattern_max_len,
1012 };
1013 NLA_PUT(msg, NL80211_WOWLAN_TRIG_PKT_PATTERN,
1014 sizeof(pat), &pat);
1015 }
1016
1017 nla_nest_end(msg, nl_wowlan);
1018 }
1019
Johannes Berg7527a782011-05-13 10:58:57 +02001020 if (nl80211_put_iftypes(msg, NL80211_ATTR_SOFTWARE_IFTYPES,
1021 dev->wiphy.software_iftypes))
1022 goto nla_put_failure;
1023
1024 if (nl80211_put_iface_combinations(&dev->wiphy, msg))
1025 goto nla_put_failure;
1026
Johannes Berg562a7482011-11-07 12:39:33 +01001027 if (dev->wiphy.flags & WIPHY_FLAG_HAVE_AP_SME)
1028 NLA_PUT_U32(msg, NL80211_ATTR_DEVICE_AP_SME,
1029 dev->wiphy.ap_sme_capa);
1030
Johannes Berg1f074bd2011-11-06 14:13:33 +01001031 NLA_PUT_U32(msg, NL80211_ATTR_FEATURE_FLAGS, dev->wiphy.features);
1032
Johannes Berg55682962007-09-20 13:09:35 -04001033 return genlmsg_end(msg, hdr);
1034
1035 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001036 genlmsg_cancel(msg, hdr);
1037 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001038}
1039
1040static int nl80211_dump_wiphy(struct sk_buff *skb, struct netlink_callback *cb)
1041{
1042 int idx = 0;
1043 int start = cb->args[0];
1044 struct cfg80211_registered_device *dev;
1045
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001046 mutex_lock(&cfg80211_mutex);
Johannes Berg79c97e92009-07-07 03:56:12 +02001047 list_for_each_entry(dev, &cfg80211_rdev_list, list) {
Johannes Berg463d0182009-07-14 00:33:35 +02001048 if (!net_eq(wiphy_net(&dev->wiphy), sock_net(skb->sk)))
1049 continue;
Julius Volzb4637272008-07-08 14:02:19 +02001050 if (++idx <= start)
Johannes Berg55682962007-09-20 13:09:35 -04001051 continue;
1052 if (nl80211_send_wiphy(skb, NETLINK_CB(cb->skb).pid,
1053 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Julius Volzb4637272008-07-08 14:02:19 +02001054 dev) < 0) {
1055 idx--;
Johannes Berg55682962007-09-20 13:09:35 -04001056 break;
Julius Volzb4637272008-07-08 14:02:19 +02001057 }
Johannes Berg55682962007-09-20 13:09:35 -04001058 }
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001059 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001060
1061 cb->args[0] = idx;
1062
1063 return skb->len;
1064}
1065
1066static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info)
1067{
1068 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001069 struct cfg80211_registered_device *dev = info->user_ptr[0];
Johannes Berg55682962007-09-20 13:09:35 -04001070
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001071 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001072 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001073 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001074
Johannes Berg4c476992010-10-04 21:36:35 +02001075 if (nl80211_send_wiphy(msg, info->snd_pid, info->snd_seq, 0, dev) < 0) {
1076 nlmsg_free(msg);
1077 return -ENOBUFS;
1078 }
Johannes Berg55682962007-09-20 13:09:35 -04001079
Johannes Berg134e6372009-07-10 09:51:34 +00001080 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001081}
1082
Jouni Malinen31888482008-10-30 16:59:24 +02001083static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = {
1084 [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 },
1085 [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 },
1086 [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 },
1087 [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 },
1088 [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 },
1089};
1090
1091static int parse_txq_params(struct nlattr *tb[],
1092 struct ieee80211_txq_params *txq_params)
1093{
1094 if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] ||
1095 !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] ||
1096 !tb[NL80211_TXQ_ATTR_AIFS])
1097 return -EINVAL;
1098
1099 txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]);
1100 txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]);
1101 txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]);
1102 txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]);
1103 txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]);
1104
1105 return 0;
1106}
1107
Johannes Bergf444de02010-05-05 15:25:02 +02001108static bool nl80211_can_set_dev_channel(struct wireless_dev *wdev)
1109{
1110 /*
1111 * You can only set the channel explicitly for AP, mesh
1112 * and WDS type interfaces; all others have their channel
1113 * managed via their respective "establish a connection"
1114 * command (connect, join, ...)
1115 *
1116 * Monitors are special as they are normally slaved to
1117 * whatever else is going on, so they behave as though
1118 * you tried setting the wiphy channel itself.
1119 */
1120 return !wdev ||
1121 wdev->iftype == NL80211_IFTYPE_AP ||
1122 wdev->iftype == NL80211_IFTYPE_WDS ||
1123 wdev->iftype == NL80211_IFTYPE_MESH_POINT ||
Johannes Berg074ac8d2010-09-16 14:58:22 +02001124 wdev->iftype == NL80211_IFTYPE_MONITOR ||
1125 wdev->iftype == NL80211_IFTYPE_P2P_GO;
Johannes Bergf444de02010-05-05 15:25:02 +02001126}
1127
1128static int __nl80211_set_channel(struct cfg80211_registered_device *rdev,
1129 struct wireless_dev *wdev,
1130 struct genl_info *info)
1131{
1132 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
1133 u32 freq;
1134 int result;
1135
1136 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
1137 return -EINVAL;
1138
1139 if (!nl80211_can_set_dev_channel(wdev))
1140 return -EOPNOTSUPP;
1141
1142 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
1143 channel_type = nla_get_u32(info->attrs[
1144 NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
1145 if (channel_type != NL80211_CHAN_NO_HT &&
1146 channel_type != NL80211_CHAN_HT20 &&
1147 channel_type != NL80211_CHAN_HT40PLUS &&
1148 channel_type != NL80211_CHAN_HT40MINUS)
1149 return -EINVAL;
1150 }
1151
1152 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
1153
1154 mutex_lock(&rdev->devlist_mtx);
1155 if (wdev) {
1156 wdev_lock(wdev);
1157 result = cfg80211_set_freq(rdev, wdev, freq, channel_type);
1158 wdev_unlock(wdev);
1159 } else {
1160 result = cfg80211_set_freq(rdev, NULL, freq, channel_type);
1161 }
1162 mutex_unlock(&rdev->devlist_mtx);
1163
1164 return result;
1165}
1166
1167static int nl80211_set_channel(struct sk_buff *skb, struct genl_info *info)
1168{
Johannes Berg4c476992010-10-04 21:36:35 +02001169 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1170 struct net_device *netdev = info->user_ptr[1];
Johannes Bergf444de02010-05-05 15:25:02 +02001171
Johannes Berg4c476992010-10-04 21:36:35 +02001172 return __nl80211_set_channel(rdev, netdev->ieee80211_ptr, info);
Johannes Bergf444de02010-05-05 15:25:02 +02001173}
1174
Bill Jordane8347eb2010-10-01 13:54:28 -04001175static int nl80211_set_wds_peer(struct sk_buff *skb, struct genl_info *info)
1176{
Johannes Berg43b19952010-10-07 13:10:30 +02001177 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1178 struct net_device *dev = info->user_ptr[1];
1179 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berg388ac772010-10-07 13:11:09 +02001180 const u8 *bssid;
Bill Jordane8347eb2010-10-01 13:54:28 -04001181
1182 if (!info->attrs[NL80211_ATTR_MAC])
1183 return -EINVAL;
1184
Johannes Berg43b19952010-10-07 13:10:30 +02001185 if (netif_running(dev))
1186 return -EBUSY;
Bill Jordane8347eb2010-10-01 13:54:28 -04001187
Johannes Berg43b19952010-10-07 13:10:30 +02001188 if (!rdev->ops->set_wds_peer)
1189 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001190
Johannes Berg43b19952010-10-07 13:10:30 +02001191 if (wdev->iftype != NL80211_IFTYPE_WDS)
1192 return -EOPNOTSUPP;
Bill Jordane8347eb2010-10-01 13:54:28 -04001193
1194 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg43b19952010-10-07 13:10:30 +02001195 return rdev->ops->set_wds_peer(wdev->wiphy, dev, bssid);
Bill Jordane8347eb2010-10-01 13:54:28 -04001196}
1197
1198
Johannes Berg55682962007-09-20 13:09:35 -04001199static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info)
1200{
1201 struct cfg80211_registered_device *rdev;
Johannes Bergf444de02010-05-05 15:25:02 +02001202 struct net_device *netdev = NULL;
1203 struct wireless_dev *wdev;
Bill Jordana1e567c2010-09-10 11:22:32 -04001204 int result = 0, rem_txq_params = 0;
Jouni Malinen31888482008-10-30 16:59:24 +02001205 struct nlattr *nl_txq_params;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001206 u32 changed;
1207 u8 retry_short = 0, retry_long = 0;
1208 u32 frag_threshold = 0, rts_threshold = 0;
Lukáš Turek81077e82009-12-21 22:50:47 +01001209 u8 coverage_class = 0;
Johannes Berg55682962007-09-20 13:09:35 -04001210
Johannes Bergf444de02010-05-05 15:25:02 +02001211 /*
1212 * Try to find the wiphy and netdev. Normally this
1213 * function shouldn't need the netdev, but this is
1214 * done for backward compatibility -- previously
1215 * setting the channel was done per wiphy, but now
1216 * it is per netdev. Previous userland like hostapd
1217 * also passed a netdev to set_wiphy, so that it is
1218 * possible to let that go to the right netdev!
1219 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001220 mutex_lock(&cfg80211_mutex);
1221
Johannes Bergf444de02010-05-05 15:25:02 +02001222 if (info->attrs[NL80211_ATTR_IFINDEX]) {
1223 int ifindex = nla_get_u32(info->attrs[NL80211_ATTR_IFINDEX]);
1224
1225 netdev = dev_get_by_index(genl_info_net(info), ifindex);
1226 if (netdev && netdev->ieee80211_ptr) {
1227 rdev = wiphy_to_dev(netdev->ieee80211_ptr->wiphy);
1228 mutex_lock(&rdev->mtx);
1229 } else
1230 netdev = NULL;
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001231 }
1232
Johannes Bergf444de02010-05-05 15:25:02 +02001233 if (!netdev) {
1234 rdev = __cfg80211_rdev_from_info(info);
1235 if (IS_ERR(rdev)) {
1236 mutex_unlock(&cfg80211_mutex);
Johannes Berg4c476992010-10-04 21:36:35 +02001237 return PTR_ERR(rdev);
Johannes Bergf444de02010-05-05 15:25:02 +02001238 }
1239 wdev = NULL;
1240 netdev = NULL;
1241 result = 0;
1242
1243 mutex_lock(&rdev->mtx);
1244 } else if (netif_running(netdev) &&
1245 nl80211_can_set_dev_channel(netdev->ieee80211_ptr))
1246 wdev = netdev->ieee80211_ptr;
1247 else
1248 wdev = NULL;
1249
1250 /*
1251 * end workaround code, by now the rdev is available
1252 * and locked, and wdev may or may not be NULL.
1253 */
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001254
1255 if (info->attrs[NL80211_ATTR_WIPHY_NAME])
Jouni Malinen31888482008-10-30 16:59:24 +02001256 result = cfg80211_dev_rename(
1257 rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME]));
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001258
1259 mutex_unlock(&cfg80211_mutex);
1260
1261 if (result)
1262 goto bad_res;
Johannes Berg55682962007-09-20 13:09:35 -04001263
Jouni Malinen31888482008-10-30 16:59:24 +02001264 if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) {
1265 struct ieee80211_txq_params txq_params;
1266 struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1];
1267
1268 if (!rdev->ops->set_txq_params) {
1269 result = -EOPNOTSUPP;
1270 goto bad_res;
1271 }
1272
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001273 if (!netdev) {
1274 result = -EINVAL;
1275 goto bad_res;
1276 }
1277
Johannes Berg133a3ff2011-11-03 14:50:13 +01001278 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
1279 netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO) {
1280 result = -EINVAL;
1281 goto bad_res;
1282 }
1283
Jouni Malinen31888482008-10-30 16:59:24 +02001284 nla_for_each_nested(nl_txq_params,
1285 info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS],
1286 rem_txq_params) {
1287 nla_parse(tb, NL80211_TXQ_ATTR_MAX,
1288 nla_data(nl_txq_params),
1289 nla_len(nl_txq_params),
1290 txq_params_policy);
1291 result = parse_txq_params(tb, &txq_params);
1292 if (result)
1293 goto bad_res;
1294
1295 result = rdev->ops->set_txq_params(&rdev->wiphy,
Eliad Pellerf70f01c2011-09-25 20:06:53 +03001296 netdev,
Jouni Malinen31888482008-10-30 16:59:24 +02001297 &txq_params);
1298 if (result)
1299 goto bad_res;
1300 }
1301 }
1302
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001303 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
Johannes Bergf444de02010-05-05 15:25:02 +02001304 result = __nl80211_set_channel(rdev, wdev, info);
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001305 if (result)
1306 goto bad_res;
1307 }
1308
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001309 if (info->attrs[NL80211_ATTR_WIPHY_TX_POWER_SETTING]) {
1310 enum nl80211_tx_power_setting type;
1311 int idx, mbm = 0;
1312
1313 if (!rdev->ops->set_tx_power) {
Jiri Slaby60ea3852010-07-07 15:02:46 +02001314 result = -EOPNOTSUPP;
Juuso Oikarinen98d2ff82010-06-23 12:12:38 +03001315 goto bad_res;
1316 }
1317
1318 idx = NL80211_ATTR_WIPHY_TX_POWER_SETTING;
1319 type = nla_get_u32(info->attrs[idx]);
1320
1321 if (!info->attrs[NL80211_ATTR_WIPHY_TX_POWER_LEVEL] &&
1322 (type != NL80211_TX_POWER_AUTOMATIC)) {
1323 result = -EINVAL;
1324 goto bad_res;
1325 }
1326
1327 if (type != NL80211_TX_POWER_AUTOMATIC) {
1328 idx = NL80211_ATTR_WIPHY_TX_POWER_LEVEL;
1329 mbm = nla_get_u32(info->attrs[idx]);
1330 }
1331
1332 result = rdev->ops->set_tx_power(&rdev->wiphy, type, mbm);
1333 if (result)
1334 goto bad_res;
1335 }
1336
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001337 if (info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX] &&
1338 info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]) {
1339 u32 tx_ant, rx_ant;
Bruno Randolf7f531e02010-12-16 11:30:22 +09001340 if ((!rdev->wiphy.available_antennas_tx &&
1341 !rdev->wiphy.available_antennas_rx) ||
1342 !rdev->ops->set_antenna) {
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001343 result = -EOPNOTSUPP;
1344 goto bad_res;
1345 }
1346
1347 tx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_TX]);
1348 rx_ant = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_ANTENNA_RX]);
1349
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001350 /* reject antenna configurations which don't match the
Bruno Randolf7f531e02010-12-16 11:30:22 +09001351 * available antenna masks, except for the "all" mask */
1352 if ((~tx_ant && (tx_ant & ~rdev->wiphy.available_antennas_tx)) ||
1353 (~rx_ant && (rx_ant & ~rdev->wiphy.available_antennas_rx))) {
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001354 result = -EINVAL;
1355 goto bad_res;
1356 }
1357
Bruno Randolf7f531e02010-12-16 11:30:22 +09001358 tx_ant = tx_ant & rdev->wiphy.available_antennas_tx;
1359 rx_ant = rx_ant & rdev->wiphy.available_antennas_rx;
Bruno Randolfa7ffac92010-12-08 13:59:24 +09001360
Bruno Randolfafe0cbf2010-11-10 12:50:50 +09001361 result = rdev->ops->set_antenna(&rdev->wiphy, tx_ant, rx_ant);
1362 if (result)
1363 goto bad_res;
1364 }
1365
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001366 changed = 0;
1367
1368 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]) {
1369 retry_short = nla_get_u8(
1370 info->attrs[NL80211_ATTR_WIPHY_RETRY_SHORT]);
1371 if (retry_short == 0) {
1372 result = -EINVAL;
1373 goto bad_res;
1374 }
1375 changed |= WIPHY_PARAM_RETRY_SHORT;
1376 }
1377
1378 if (info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]) {
1379 retry_long = nla_get_u8(
1380 info->attrs[NL80211_ATTR_WIPHY_RETRY_LONG]);
1381 if (retry_long == 0) {
1382 result = -EINVAL;
1383 goto bad_res;
1384 }
1385 changed |= WIPHY_PARAM_RETRY_LONG;
1386 }
1387
1388 if (info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]) {
1389 frag_threshold = nla_get_u32(
1390 info->attrs[NL80211_ATTR_WIPHY_FRAG_THRESHOLD]);
1391 if (frag_threshold < 256) {
1392 result = -EINVAL;
1393 goto bad_res;
1394 }
1395 if (frag_threshold != (u32) -1) {
1396 /*
1397 * Fragments (apart from the last one) are required to
1398 * have even length. Make the fragmentation code
1399 * simpler by stripping LSB should someone try to use
1400 * odd threshold value.
1401 */
1402 frag_threshold &= ~0x1;
1403 }
1404 changed |= WIPHY_PARAM_FRAG_THRESHOLD;
1405 }
1406
1407 if (info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]) {
1408 rts_threshold = nla_get_u32(
1409 info->attrs[NL80211_ATTR_WIPHY_RTS_THRESHOLD]);
1410 changed |= WIPHY_PARAM_RTS_THRESHOLD;
1411 }
1412
Lukáš Turek81077e82009-12-21 22:50:47 +01001413 if (info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]) {
1414 coverage_class = nla_get_u8(
1415 info->attrs[NL80211_ATTR_WIPHY_COVERAGE_CLASS]);
1416 changed |= WIPHY_PARAM_COVERAGE_CLASS;
1417 }
1418
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001419 if (changed) {
1420 u8 old_retry_short, old_retry_long;
1421 u32 old_frag_threshold, old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001422 u8 old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001423
1424 if (!rdev->ops->set_wiphy_params) {
1425 result = -EOPNOTSUPP;
1426 goto bad_res;
1427 }
1428
1429 old_retry_short = rdev->wiphy.retry_short;
1430 old_retry_long = rdev->wiphy.retry_long;
1431 old_frag_threshold = rdev->wiphy.frag_threshold;
1432 old_rts_threshold = rdev->wiphy.rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001433 old_coverage_class = rdev->wiphy.coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001434
1435 if (changed & WIPHY_PARAM_RETRY_SHORT)
1436 rdev->wiphy.retry_short = retry_short;
1437 if (changed & WIPHY_PARAM_RETRY_LONG)
1438 rdev->wiphy.retry_long = retry_long;
1439 if (changed & WIPHY_PARAM_FRAG_THRESHOLD)
1440 rdev->wiphy.frag_threshold = frag_threshold;
1441 if (changed & WIPHY_PARAM_RTS_THRESHOLD)
1442 rdev->wiphy.rts_threshold = rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001443 if (changed & WIPHY_PARAM_COVERAGE_CLASS)
1444 rdev->wiphy.coverage_class = coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001445
1446 result = rdev->ops->set_wiphy_params(&rdev->wiphy, changed);
1447 if (result) {
1448 rdev->wiphy.retry_short = old_retry_short;
1449 rdev->wiphy.retry_long = old_retry_long;
1450 rdev->wiphy.frag_threshold = old_frag_threshold;
1451 rdev->wiphy.rts_threshold = old_rts_threshold;
Lukáš Turek81077e82009-12-21 22:50:47 +01001452 rdev->wiphy.coverage_class = old_coverage_class;
Jouni Malinenb9a5f8ca2009-04-20 18:39:05 +02001453 }
1454 }
Jouni Malinen72bdcf32008-11-26 16:15:24 +02001455
Johannes Berg306d6112008-12-08 12:39:04 +01001456 bad_res:
Johannes Berg4bbf4d52009-03-24 09:35:46 +01001457 mutex_unlock(&rdev->mtx);
Johannes Bergf444de02010-05-05 15:25:02 +02001458 if (netdev)
1459 dev_put(netdev);
Johannes Berg55682962007-09-20 13:09:35 -04001460 return result;
1461}
1462
1463
1464static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags,
Johannes Bergd7264052009-04-19 16:23:20 +02001465 struct cfg80211_registered_device *rdev,
Johannes Berg55682962007-09-20 13:09:35 -04001466 struct net_device *dev)
1467{
1468 void *hdr;
1469
1470 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_INTERFACE);
1471 if (!hdr)
1472 return -1;
1473
1474 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
Johannes Bergd7264052009-04-19 16:23:20 +02001475 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg55682962007-09-20 13:09:35 -04001476 NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name);
Johannes Berg60719ff2008-09-16 14:55:09 +02001477 NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001478
1479 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION,
1480 rdev->devlist_generation ^
1481 (cfg80211_rdev_list_generation << 2));
1482
Johannes Berg55682962007-09-20 13:09:35 -04001483 return genlmsg_end(msg, hdr);
1484
1485 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07001486 genlmsg_cancel(msg, hdr);
1487 return -EMSGSIZE;
Johannes Berg55682962007-09-20 13:09:35 -04001488}
1489
1490static int nl80211_dump_interface(struct sk_buff *skb, struct netlink_callback *cb)
1491{
1492 int wp_idx = 0;
1493 int if_idx = 0;
1494 int wp_start = cb->args[0];
1495 int if_start = cb->args[1];
Johannes Bergf5ea9122009-08-07 16:17:38 +02001496 struct cfg80211_registered_device *rdev;
Johannes Berg55682962007-09-20 13:09:35 -04001497 struct wireless_dev *wdev;
1498
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001499 mutex_lock(&cfg80211_mutex);
Johannes Bergf5ea9122009-08-07 16:17:38 +02001500 list_for_each_entry(rdev, &cfg80211_rdev_list, list) {
1501 if (!net_eq(wiphy_net(&rdev->wiphy), sock_net(skb->sk)))
Johannes Berg463d0182009-07-14 00:33:35 +02001502 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001503 if (wp_idx < wp_start) {
1504 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001505 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001506 }
Johannes Berg55682962007-09-20 13:09:35 -04001507 if_idx = 0;
1508
Johannes Bergf5ea9122009-08-07 16:17:38 +02001509 mutex_lock(&rdev->devlist_mtx);
1510 list_for_each_entry(wdev, &rdev->netdev_list, list) {
Johannes Bergbba95fe2008-07-29 13:22:51 +02001511 if (if_idx < if_start) {
1512 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001513 continue;
Johannes Bergbba95fe2008-07-29 13:22:51 +02001514 }
Johannes Berg55682962007-09-20 13:09:35 -04001515 if (nl80211_send_iface(skb, NETLINK_CB(cb->skb).pid,
1516 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Bergf5ea9122009-08-07 16:17:38 +02001517 rdev, wdev->netdev) < 0) {
1518 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001519 goto out;
1520 }
1521 if_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001522 }
Johannes Bergf5ea9122009-08-07 16:17:38 +02001523 mutex_unlock(&rdev->devlist_mtx);
Johannes Bergbba95fe2008-07-29 13:22:51 +02001524
1525 wp_idx++;
Johannes Berg55682962007-09-20 13:09:35 -04001526 }
Johannes Bergbba95fe2008-07-29 13:22:51 +02001527 out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05001528 mutex_unlock(&cfg80211_mutex);
Johannes Berg55682962007-09-20 13:09:35 -04001529
1530 cb->args[0] = wp_idx;
1531 cb->args[1] = if_idx;
1532
1533 return skb->len;
1534}
1535
1536static int nl80211_get_interface(struct sk_buff *skb, struct genl_info *info)
1537{
1538 struct sk_buff *msg;
Johannes Berg4c476992010-10-04 21:36:35 +02001539 struct cfg80211_registered_device *dev = info->user_ptr[0];
1540 struct net_device *netdev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001541
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001542 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04001543 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02001544 return -ENOMEM;
Johannes Berg55682962007-09-20 13:09:35 -04001545
Johannes Bergd7264052009-04-19 16:23:20 +02001546 if (nl80211_send_iface(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02001547 dev, netdev) < 0) {
1548 nlmsg_free(msg);
1549 return -ENOBUFS;
1550 }
Johannes Berg55682962007-09-20 13:09:35 -04001551
Johannes Berg134e6372009-07-10 09:51:34 +00001552 return genlmsg_reply(msg, info);
Johannes Berg55682962007-09-20 13:09:35 -04001553}
1554
Michael Wu66f7ac52008-01-31 19:48:22 +01001555static const struct nla_policy mntr_flags_policy[NL80211_MNTR_FLAG_MAX + 1] = {
1556 [NL80211_MNTR_FLAG_FCSFAIL] = { .type = NLA_FLAG },
1557 [NL80211_MNTR_FLAG_PLCPFAIL] = { .type = NLA_FLAG },
1558 [NL80211_MNTR_FLAG_CONTROL] = { .type = NLA_FLAG },
1559 [NL80211_MNTR_FLAG_OTHER_BSS] = { .type = NLA_FLAG },
1560 [NL80211_MNTR_FLAG_COOK_FRAMES] = { .type = NLA_FLAG },
1561};
1562
1563static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
1564{
1565 struct nlattr *flags[NL80211_MNTR_FLAG_MAX + 1];
1566 int flag;
1567
1568 *mntrflags = 0;
1569
1570 if (!nla)
1571 return -EINVAL;
1572
1573 if (nla_parse_nested(flags, NL80211_MNTR_FLAG_MAX,
1574 nla, mntr_flags_policy))
1575 return -EINVAL;
1576
1577 for (flag = 1; flag <= NL80211_MNTR_FLAG_MAX; flag++)
1578 if (flags[flag])
1579 *mntrflags |= (1<<flag);
1580
1581 return 0;
1582}
1583
Johannes Berg9bc383d2009-11-19 11:55:19 +01001584static int nl80211_valid_4addr(struct cfg80211_registered_device *rdev,
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001585 struct net_device *netdev, u8 use_4addr,
1586 enum nl80211_iftype iftype)
Johannes Berg9bc383d2009-11-19 11:55:19 +01001587{
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001588 if (!use_4addr) {
Jiri Pirkof350a0a82010-06-15 06:50:45 +00001589 if (netdev && (netdev->priv_flags & IFF_BRIDGE_PORT))
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001590 return -EBUSY;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001591 return 0;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001592 }
Johannes Berg9bc383d2009-11-19 11:55:19 +01001593
1594 switch (iftype) {
1595 case NL80211_IFTYPE_AP_VLAN:
1596 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_AP)
1597 return 0;
1598 break;
1599 case NL80211_IFTYPE_STATION:
1600 if (rdev->wiphy.flags & WIPHY_FLAG_4ADDR_STATION)
1601 return 0;
1602 break;
1603 default:
1604 break;
1605 }
1606
1607 return -EOPNOTSUPP;
1608}
1609
Johannes Berg55682962007-09-20 13:09:35 -04001610static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
1611{
Johannes Berg4c476992010-10-04 21:36:35 +02001612 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001613 struct vif_params params;
Johannes Berge36d56b2009-06-09 21:04:43 +02001614 int err;
Johannes Berg04a773a2009-04-19 21:24:32 +02001615 enum nl80211_iftype otype, ntype;
Johannes Berg4c476992010-10-04 21:36:35 +02001616 struct net_device *dev = info->user_ptr[1];
Johannes Berg92ffe052008-09-16 20:39:36 +02001617 u32 _flags, *flags = NULL;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001618 bool change = false;
Johannes Berg55682962007-09-20 13:09:35 -04001619
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001620 memset(&params, 0, sizeof(params));
1621
Johannes Berg04a773a2009-04-19 21:24:32 +02001622 otype = ntype = dev->ieee80211_ptr->iftype;
Johannes Berg55682962007-09-20 13:09:35 -04001623
Johannes Berg723b0382008-09-16 20:22:09 +02001624 if (info->attrs[NL80211_ATTR_IFTYPE]) {
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001625 ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
Johannes Berg04a773a2009-04-19 21:24:32 +02001626 if (otype != ntype)
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001627 change = true;
Johannes Berg4c476992010-10-04 21:36:35 +02001628 if (ntype > NL80211_IFTYPE_MAX)
1629 return -EINVAL;
Johannes Berg723b0382008-09-16 20:22:09 +02001630 }
1631
Johannes Berg92ffe052008-09-16 20:39:36 +02001632 if (info->attrs[NL80211_ATTR_MESH_ID]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01001633 struct wireless_dev *wdev = dev->ieee80211_ptr;
1634
Johannes Berg4c476992010-10-04 21:36:35 +02001635 if (ntype != NL80211_IFTYPE_MESH_POINT)
1636 return -EINVAL;
Johannes Berg29cbe682010-12-03 09:20:44 +01001637 if (netif_running(dev))
1638 return -EBUSY;
1639
1640 wdev_lock(wdev);
1641 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1642 IEEE80211_MAX_MESH_ID_LEN);
1643 wdev->mesh_id_up_len =
1644 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1645 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1646 wdev->mesh_id_up_len);
1647 wdev_unlock(wdev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001648 }
1649
Felix Fietkau8b787642009-11-10 18:53:10 +01001650 if (info->attrs[NL80211_ATTR_4ADDR]) {
1651 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
1652 change = true;
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001653 err = nl80211_valid_4addr(rdev, dev, params.use_4addr, ntype);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001654 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001655 return err;
Felix Fietkau8b787642009-11-10 18:53:10 +01001656 } else {
1657 params.use_4addr = -1;
1658 }
1659
Johannes Berg92ffe052008-09-16 20:39:36 +02001660 if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) {
Johannes Berg4c476992010-10-04 21:36:35 +02001661 if (ntype != NL80211_IFTYPE_MONITOR)
1662 return -EINVAL;
Johannes Berg92ffe052008-09-16 20:39:36 +02001663 err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS],
1664 &_flags);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001665 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001666 return err;
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001667
1668 flags = &_flags;
1669 change = true;
Johannes Berg92ffe052008-09-16 20:39:36 +02001670 }
Johannes Berg3b858752009-03-12 09:55:09 +01001671
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001672 if (change)
Johannes Berg3d54d252009-08-21 14:51:05 +02001673 err = cfg80211_change_iface(rdev, dev, ntype, flags, &params);
Johannes Bergac7f9cf2009-03-21 17:07:59 +01001674 else
1675 err = 0;
Johannes Berg60719ff2008-09-16 14:55:09 +02001676
Johannes Berg9bc383d2009-11-19 11:55:19 +01001677 if (!err && params.use_4addr != -1)
1678 dev->ieee80211_ptr->use_4addr = params.use_4addr;
1679
Johannes Berg55682962007-09-20 13:09:35 -04001680 return err;
1681}
1682
1683static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
1684{
Johannes Berg4c476992010-10-04 21:36:35 +02001685 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001686 struct vif_params params;
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001687 struct net_device *dev;
Johannes Berg55682962007-09-20 13:09:35 -04001688 int err;
1689 enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
Michael Wu66f7ac52008-01-31 19:48:22 +01001690 u32 flags;
Johannes Berg55682962007-09-20 13:09:35 -04001691
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001692 memset(&params, 0, sizeof(params));
1693
Johannes Berg55682962007-09-20 13:09:35 -04001694 if (!info->attrs[NL80211_ATTR_IFNAME])
1695 return -EINVAL;
1696
1697 if (info->attrs[NL80211_ATTR_IFTYPE]) {
1698 type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
1699 if (type > NL80211_IFTYPE_MAX)
1700 return -EINVAL;
1701 }
1702
Johannes Berg79c97e92009-07-07 03:56:12 +02001703 if (!rdev->ops->add_virtual_intf ||
Johannes Berg4c476992010-10-04 21:36:35 +02001704 !(rdev->wiphy.interface_modes & (1 << type)))
1705 return -EOPNOTSUPP;
Johannes Berg55682962007-09-20 13:09:35 -04001706
Johannes Berg9bc383d2009-11-19 11:55:19 +01001707 if (info->attrs[NL80211_ATTR_4ADDR]) {
Felix Fietkau8b787642009-11-10 18:53:10 +01001708 params.use_4addr = !!nla_get_u8(info->attrs[NL80211_ATTR_4ADDR]);
Johannes Bergad4bb6f2009-11-19 00:56:30 +01001709 err = nl80211_valid_4addr(rdev, NULL, params.use_4addr, type);
Johannes Berg9bc383d2009-11-19 11:55:19 +01001710 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02001711 return err;
Johannes Berg9bc383d2009-11-19 11:55:19 +01001712 }
Felix Fietkau8b787642009-11-10 18:53:10 +01001713
Michael Wu66f7ac52008-01-31 19:48:22 +01001714 err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
1715 info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
1716 &flags);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001717 dev = rdev->ops->add_virtual_intf(&rdev->wiphy,
Michael Wu66f7ac52008-01-31 19:48:22 +01001718 nla_data(info->attrs[NL80211_ATTR_IFNAME]),
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001719 type, err ? NULL : &flags, &params);
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001720 if (IS_ERR(dev))
1721 return PTR_ERR(dev);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01001722
Johannes Berg29cbe682010-12-03 09:20:44 +01001723 if (type == NL80211_IFTYPE_MESH_POINT &&
1724 info->attrs[NL80211_ATTR_MESH_ID]) {
1725 struct wireless_dev *wdev = dev->ieee80211_ptr;
1726
1727 wdev_lock(wdev);
1728 BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN !=
1729 IEEE80211_MAX_MESH_ID_LEN);
1730 wdev->mesh_id_up_len =
1731 nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
1732 memcpy(wdev->ssid, nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
1733 wdev->mesh_id_up_len);
1734 wdev_unlock(wdev);
1735 }
1736
Johannes Bergf9e10ce2010-12-03 09:20:42 +01001737 return 0;
Johannes Berg55682962007-09-20 13:09:35 -04001738}
1739
1740static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info)
1741{
Johannes Berg4c476992010-10-04 21:36:35 +02001742 struct cfg80211_registered_device *rdev = info->user_ptr[0];
1743 struct net_device *dev = info->user_ptr[1];
Johannes Berg55682962007-09-20 13:09:35 -04001744
Johannes Berg4c476992010-10-04 21:36:35 +02001745 if (!rdev->ops->del_virtual_intf)
1746 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001747
Johannes Berg4c476992010-10-04 21:36:35 +02001748 return rdev->ops->del_virtual_intf(&rdev->wiphy, dev);
Johannes Berg55682962007-09-20 13:09:35 -04001749}
1750
Johannes Berg41ade002007-12-19 02:03:29 +01001751struct get_key_cookie {
1752 struct sk_buff *msg;
1753 int error;
Johannes Bergb9454e82009-07-08 13:29:08 +02001754 int idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001755};
1756
1757static void get_key_callback(void *c, struct key_params *params)
1758{
Johannes Bergb9454e82009-07-08 13:29:08 +02001759 struct nlattr *key;
Johannes Berg41ade002007-12-19 02:03:29 +01001760 struct get_key_cookie *cookie = c;
1761
1762 if (params->key)
1763 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA,
1764 params->key_len, params->key);
1765
1766 if (params->seq)
1767 NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ,
1768 params->seq_len, params->seq);
1769
1770 if (params->cipher)
1771 NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER,
1772 params->cipher);
1773
Johannes Bergb9454e82009-07-08 13:29:08 +02001774 key = nla_nest_start(cookie->msg, NL80211_ATTR_KEY);
1775 if (!key)
1776 goto nla_put_failure;
1777
1778 if (params->key)
1779 NLA_PUT(cookie->msg, NL80211_KEY_DATA,
1780 params->key_len, params->key);
1781
1782 if (params->seq)
1783 NLA_PUT(cookie->msg, NL80211_KEY_SEQ,
1784 params->seq_len, params->seq);
1785
1786 if (params->cipher)
1787 NLA_PUT_U32(cookie->msg, NL80211_KEY_CIPHER,
1788 params->cipher);
1789
1790 NLA_PUT_U8(cookie->msg, NL80211_ATTR_KEY_IDX, cookie->idx);
1791
1792 nla_nest_end(cookie->msg, key);
1793
Johannes Berg41ade002007-12-19 02:03:29 +01001794 return;
1795 nla_put_failure:
1796 cookie->error = 1;
1797}
1798
1799static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
1800{
Johannes Berg4c476992010-10-04 21:36:35 +02001801 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001802 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001803 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001804 u8 key_idx = 0;
Johannes Berge31b8212010-10-05 19:39:30 +02001805 const u8 *mac_addr = NULL;
1806 bool pairwise;
Johannes Berg41ade002007-12-19 02:03:29 +01001807 struct get_key_cookie cookie = {
1808 .error = 0,
1809 };
1810 void *hdr;
1811 struct sk_buff *msg;
1812
1813 if (info->attrs[NL80211_ATTR_KEY_IDX])
1814 key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]);
1815
Jouni Malinen3cfcf6ac2009-01-08 13:32:02 +02001816 if (key_idx > 5)
Johannes Berg41ade002007-12-19 02:03:29 +01001817 return -EINVAL;
1818
1819 if (info->attrs[NL80211_ATTR_MAC])
1820 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1821
Johannes Berge31b8212010-10-05 19:39:30 +02001822 pairwise = !!mac_addr;
1823 if (info->attrs[NL80211_ATTR_KEY_TYPE]) {
1824 u32 kt = nla_get_u32(info->attrs[NL80211_ATTR_KEY_TYPE]);
1825 if (kt >= NUM_NL80211_KEYTYPES)
1826 return -EINVAL;
1827 if (kt != NL80211_KEYTYPE_GROUP &&
1828 kt != NL80211_KEYTYPE_PAIRWISE)
1829 return -EINVAL;
1830 pairwise = kt == NL80211_KEYTYPE_PAIRWISE;
1831 }
1832
Johannes Berg4c476992010-10-04 21:36:35 +02001833 if (!rdev->ops->get_key)
1834 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01001835
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07001836 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02001837 if (!msg)
1838 return -ENOMEM;
Johannes Berg41ade002007-12-19 02:03:29 +01001839
1840 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
1841 NL80211_CMD_NEW_KEY);
Johannes Berg4c476992010-10-04 21:36:35 +02001842 if (IS_ERR(hdr))
1843 return PTR_ERR(hdr);
Johannes Berg41ade002007-12-19 02:03:29 +01001844
1845 cookie.msg = msg;
Johannes Bergb9454e82009-07-08 13:29:08 +02001846 cookie.idx = key_idx;
Johannes Berg41ade002007-12-19 02:03:29 +01001847
1848 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
1849 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx);
1850 if (mac_addr)
1851 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
1852
Johannes Berge31b8212010-10-05 19:39:30 +02001853 if (pairwise && mac_addr &&
1854 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
1855 return -ENOENT;
1856
1857 err = rdev->ops->get_key(&rdev->wiphy, dev, key_idx, pairwise,
1858 mac_addr, &cookie, get_key_callback);
Johannes Berg41ade002007-12-19 02:03:29 +01001859
1860 if (err)
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001861 goto free_msg;
Johannes Berg41ade002007-12-19 02:03:29 +01001862
1863 if (cookie.error)
1864 goto nla_put_failure;
1865
1866 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02001867 return genlmsg_reply(msg, info);
Johannes Berg41ade002007-12-19 02:03:29 +01001868
1869 nla_put_failure:
1870 err = -ENOBUFS;
Niko Jokinen6c95e2a2009-07-15 11:00:53 +03001871 free_msg:
Johannes Berg41ade002007-12-19 02:03:29 +01001872 nlmsg_free(msg);
Johannes Berg41ade002007-12-19 02:03:29 +01001873 return err;
1874}
1875
1876static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info)
1877{
Johannes Berg4c476992010-10-04 21:36:35 +02001878 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergb9454e82009-07-08 13:29:08 +02001879 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01001880 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001881 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01001882
Johannes Bergb9454e82009-07-08 13:29:08 +02001883 err = nl80211_parse_key(info, &key);
1884 if (err)
1885 return err;
1886
1887 if (key.idx < 0)
Johannes Berg41ade002007-12-19 02:03:29 +01001888 return -EINVAL;
1889
Johannes Bergb9454e82009-07-08 13:29:08 +02001890 /* only support setting default key */
1891 if (!key.def && !key.defmgmt)
Johannes Berg41ade002007-12-19 02:03:29 +01001892 return -EINVAL;
1893
Johannes Bergfffd0932009-07-08 14:22:54 +02001894 wdev_lock(dev->ieee80211_ptr);
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001895
1896 if (key.def) {
1897 if (!rdev->ops->set_default_key) {
1898 err = -EOPNOTSUPP;
1899 goto out;
1900 }
1901
1902 err = nl80211_key_allowed(dev->ieee80211_ptr);
1903 if (err)
1904 goto out;
1905
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001906 err = rdev->ops->set_default_key(&rdev->wiphy, dev, key.idx,
1907 key.def_uni, key.def_multi);
1908
1909 if (err)
1910 goto out;
Johannes Bergfffd0932009-07-08 14:22:54 +02001911
Johannes Berg3d23e342009-09-29 23:27:28 +02001912#ifdef CONFIG_CFG80211_WEXT
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001913 dev->ieee80211_ptr->wext.default_key = key.idx;
Johannes Berg08645122009-05-11 13:54:58 +02001914#endif
Johannes Bergdbd2fd62010-12-09 19:58:59 +01001915 } else {
1916 if (key.def_uni || !key.def_multi) {
1917 err = -EINVAL;
1918 goto out;
1919 }
1920
1921 if (!rdev->ops->set_default_mgmt_key) {
1922 err = -EOPNOTSUPP;
1923 goto out;
1924 }
1925
1926 err = nl80211_key_allowed(dev->ieee80211_ptr);
1927 if (err)
1928 goto out;
1929
1930 err = rdev->ops->set_default_mgmt_key(&rdev->wiphy,
1931 dev, key.idx);
1932 if (err)
1933 goto out;
1934
1935#ifdef CONFIG_CFG80211_WEXT
1936 dev->ieee80211_ptr->wext.default_mgmt_key = key.idx;
1937#endif
1938 }
1939
1940 out:
Johannes Bergfffd0932009-07-08 14:22:54 +02001941 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001942
Johannes Berg41ade002007-12-19 02:03:29 +01001943 return err;
1944}
1945
1946static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info)
1947{
Johannes Berg4c476992010-10-04 21:36:35 +02001948 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergfffd0932009-07-08 14:22:54 +02001949 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001950 struct net_device *dev = info->user_ptr[1];
Johannes Bergb9454e82009-07-08 13:29:08 +02001951 struct key_parse key;
Johannes Berge31b8212010-10-05 19:39:30 +02001952 const u8 *mac_addr = NULL;
Johannes Berg41ade002007-12-19 02:03:29 +01001953
Johannes Bergb9454e82009-07-08 13:29:08 +02001954 err = nl80211_parse_key(info, &key);
1955 if (err)
1956 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01001957
Johannes Bergb9454e82009-07-08 13:29:08 +02001958 if (!key.p.key)
Johannes Berg41ade002007-12-19 02:03:29 +01001959 return -EINVAL;
1960
Johannes Berg41ade002007-12-19 02:03:29 +01001961 if (info->attrs[NL80211_ATTR_MAC])
1962 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
1963
Johannes Berge31b8212010-10-05 19:39:30 +02001964 if (key.type == -1) {
1965 if (mac_addr)
1966 key.type = NL80211_KEYTYPE_PAIRWISE;
1967 else
1968 key.type = NL80211_KEYTYPE_GROUP;
1969 }
1970
1971 /* for now */
1972 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
1973 key.type != NL80211_KEYTYPE_GROUP)
1974 return -EINVAL;
1975
Johannes Berg4c476992010-10-04 21:36:35 +02001976 if (!rdev->ops->add_key)
1977 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01001978
Johannes Berge31b8212010-10-05 19:39:30 +02001979 if (cfg80211_validate_key_settings(rdev, &key.p, key.idx,
1980 key.type == NL80211_KEYTYPE_PAIRWISE,
1981 mac_addr))
Johannes Berg4c476992010-10-04 21:36:35 +02001982 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02001983
1984 wdev_lock(dev->ieee80211_ptr);
1985 err = nl80211_key_allowed(dev->ieee80211_ptr);
1986 if (!err)
1987 err = rdev->ops->add_key(&rdev->wiphy, dev, key.idx,
Johannes Berge31b8212010-10-05 19:39:30 +02001988 key.type == NL80211_KEYTYPE_PAIRWISE,
Johannes Bergfffd0932009-07-08 14:22:54 +02001989 mac_addr, &key.p);
1990 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg41ade002007-12-19 02:03:29 +01001991
Johannes Berg41ade002007-12-19 02:03:29 +01001992 return err;
1993}
1994
1995static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info)
1996{
Johannes Berg4c476992010-10-04 21:36:35 +02001997 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg41ade002007-12-19 02:03:29 +01001998 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02001999 struct net_device *dev = info->user_ptr[1];
Johannes Berg41ade002007-12-19 02:03:29 +01002000 u8 *mac_addr = NULL;
Johannes Bergb9454e82009-07-08 13:29:08 +02002001 struct key_parse key;
Johannes Berg41ade002007-12-19 02:03:29 +01002002
Johannes Bergb9454e82009-07-08 13:29:08 +02002003 err = nl80211_parse_key(info, &key);
2004 if (err)
2005 return err;
Johannes Berg41ade002007-12-19 02:03:29 +01002006
2007 if (info->attrs[NL80211_ATTR_MAC])
2008 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2009
Johannes Berge31b8212010-10-05 19:39:30 +02002010 if (key.type == -1) {
2011 if (mac_addr)
2012 key.type = NL80211_KEYTYPE_PAIRWISE;
2013 else
2014 key.type = NL80211_KEYTYPE_GROUP;
2015 }
2016
2017 /* for now */
2018 if (key.type != NL80211_KEYTYPE_PAIRWISE &&
2019 key.type != NL80211_KEYTYPE_GROUP)
2020 return -EINVAL;
2021
Johannes Berg4c476992010-10-04 21:36:35 +02002022 if (!rdev->ops->del_key)
2023 return -EOPNOTSUPP;
Johannes Berg41ade002007-12-19 02:03:29 +01002024
Johannes Bergfffd0932009-07-08 14:22:54 +02002025 wdev_lock(dev->ieee80211_ptr);
2026 err = nl80211_key_allowed(dev->ieee80211_ptr);
Johannes Berge31b8212010-10-05 19:39:30 +02002027
2028 if (key.type == NL80211_KEYTYPE_PAIRWISE && mac_addr &&
2029 !(rdev->wiphy.flags & WIPHY_FLAG_IBSS_RSN))
2030 err = -ENOENT;
2031
Johannes Bergfffd0932009-07-08 14:22:54 +02002032 if (!err)
Johannes Berge31b8212010-10-05 19:39:30 +02002033 err = rdev->ops->del_key(&rdev->wiphy, dev, key.idx,
2034 key.type == NL80211_KEYTYPE_PAIRWISE,
2035 mac_addr);
Johannes Berg41ade002007-12-19 02:03:29 +01002036
Johannes Berg3d23e342009-09-29 23:27:28 +02002037#ifdef CONFIG_CFG80211_WEXT
Johannes Berg08645122009-05-11 13:54:58 +02002038 if (!err) {
Johannes Bergb9454e82009-07-08 13:29:08 +02002039 if (key.idx == dev->ieee80211_ptr->wext.default_key)
Johannes Berg08645122009-05-11 13:54:58 +02002040 dev->ieee80211_ptr->wext.default_key = -1;
Johannes Bergb9454e82009-07-08 13:29:08 +02002041 else if (key.idx == dev->ieee80211_ptr->wext.default_mgmt_key)
Johannes Berg08645122009-05-11 13:54:58 +02002042 dev->ieee80211_ptr->wext.default_mgmt_key = -1;
2043 }
2044#endif
Johannes Bergfffd0932009-07-08 14:22:54 +02002045 wdev_unlock(dev->ieee80211_ptr);
Johannes Berg08645122009-05-11 13:54:58 +02002046
Johannes Berg41ade002007-12-19 02:03:29 +01002047 return err;
2048}
2049
Johannes Berged1b6cc2007-12-19 02:03:32 +01002050static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info)
2051{
2052 int (*call)(struct wiphy *wiphy, struct net_device *dev,
2053 struct beacon_parameters *info);
Johannes Berg4c476992010-10-04 21:36:35 +02002054 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2055 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002056 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002057 struct beacon_parameters params;
Johannes Berg56d18932011-05-09 18:41:15 +02002058 int haveinfo = 0, err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002059
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002060 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_BEACON_TAIL]) ||
2061 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]) ||
2062 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_PROBE_RESP]) ||
2063 !is_valid_ie_attr(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]))
Johannes Bergf4a11bb2009-03-27 12:40:28 +01002064 return -EINVAL;
2065
Johannes Berg074ac8d2010-09-16 14:58:22 +02002066 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002067 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2068 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002069
Johannes Berg56d18932011-05-09 18:41:15 +02002070 memset(&params, 0, sizeof(params));
2071
Johannes Berged1b6cc2007-12-19 02:03:32 +01002072 switch (info->genlhdr->cmd) {
2073 case NL80211_CMD_NEW_BEACON:
2074 /* these are required for NEW_BEACON */
2075 if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] ||
2076 !info->attrs[NL80211_ATTR_DTIM_PERIOD] ||
Johannes Berg4c476992010-10-04 21:36:35 +02002077 !info->attrs[NL80211_ATTR_BEACON_HEAD])
2078 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002079
Johannes Berg56d18932011-05-09 18:41:15 +02002080 params.interval =
2081 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
2082 params.dtim_period =
2083 nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]);
2084
2085 err = cfg80211_validate_beacon_int(rdev, params.interval);
2086 if (err)
2087 return err;
2088
Jouni Malinen32e9de82011-08-10 23:53:31 +03002089 /*
2090 * In theory, some of these attributes could be required for
2091 * NEW_BEACON, but since they were not used when the command was
2092 * originally added, keep them optional for old user space
2093 * programs to work with drivers that do not need the additional
2094 * information.
2095 */
2096 if (info->attrs[NL80211_ATTR_SSID]) {
2097 params.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
2098 params.ssid_len =
2099 nla_len(info->attrs[NL80211_ATTR_SSID]);
2100 if (params.ssid_len == 0 ||
2101 params.ssid_len > IEEE80211_MAX_SSID_LEN)
2102 return -EINVAL;
2103 }
2104
2105 if (info->attrs[NL80211_ATTR_HIDDEN_SSID]) {
2106 params.hidden_ssid = nla_get_u32(
2107 info->attrs[NL80211_ATTR_HIDDEN_SSID]);
2108 if (params.hidden_ssid !=
2109 NL80211_HIDDEN_SSID_NOT_IN_USE &&
2110 params.hidden_ssid !=
2111 NL80211_HIDDEN_SSID_ZERO_LEN &&
2112 params.hidden_ssid !=
2113 NL80211_HIDDEN_SSID_ZERO_CONTENTS)
2114 return -EINVAL;
2115 }
2116
Jouni Malinen5fb628e2011-08-10 23:54:35 +03002117 params.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
2118
2119 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
2120 params.auth_type = nla_get_u32(
2121 info->attrs[NL80211_ATTR_AUTH_TYPE]);
2122 if (!nl80211_valid_auth_type(params.auth_type))
2123 return -EINVAL;
2124 } else
2125 params.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
2126
2127 err = nl80211_crypto_settings(rdev, info, &params.crypto,
2128 NL80211_MAX_NR_CIPHER_SUITES);
2129 if (err)
2130 return err;
2131
Johannes Berg79c97e92009-07-07 03:56:12 +02002132 call = rdev->ops->add_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002133 break;
2134 case NL80211_CMD_SET_BEACON:
Johannes Berg79c97e92009-07-07 03:56:12 +02002135 call = rdev->ops->set_beacon;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002136 break;
2137 default:
2138 WARN_ON(1);
Johannes Berg4c476992010-10-04 21:36:35 +02002139 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002140 }
2141
Johannes Berg4c476992010-10-04 21:36:35 +02002142 if (!call)
2143 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002144
Johannes Berged1b6cc2007-12-19 02:03:32 +01002145 if (info->attrs[NL80211_ATTR_BEACON_HEAD]) {
2146 params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2147 params.head_len =
2148 nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]);
2149 haveinfo = 1;
2150 }
2151
2152 if (info->attrs[NL80211_ATTR_BEACON_TAIL]) {
2153 params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2154 params.tail_len =
2155 nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]);
2156 haveinfo = 1;
2157 }
2158
Johannes Berg4c476992010-10-04 21:36:35 +02002159 if (!haveinfo)
2160 return -EINVAL;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002161
Jouni Malinen9946ecf2011-08-10 23:55:56 +03002162 if (info->attrs[NL80211_ATTR_IE]) {
2163 params.beacon_ies = nla_data(info->attrs[NL80211_ATTR_IE]);
2164 params.beacon_ies_len = nla_len(info->attrs[NL80211_ATTR_IE]);
2165 }
2166
2167 if (info->attrs[NL80211_ATTR_IE_PROBE_RESP]) {
2168 params.proberesp_ies =
2169 nla_data(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2170 params.proberesp_ies_len =
2171 nla_len(info->attrs[NL80211_ATTR_IE_PROBE_RESP]);
2172 }
2173
2174 if (info->attrs[NL80211_ATTR_IE_ASSOC_RESP]) {
2175 params.assocresp_ies =
2176 nla_data(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2177 params.assocresp_ies_len =
2178 nla_len(info->attrs[NL80211_ATTR_IE_ASSOC_RESP]);
2179 }
2180
Arik Nemtsov00f740e2011-11-10 11:28:56 +02002181 if (info->attrs[NL80211_ATTR_PROBE_RESP]) {
2182 params.probe_resp =
2183 nla_data(info->attrs[NL80211_ATTR_PROBE_RESP]);
2184 params.probe_resp_len =
2185 nla_len(info->attrs[NL80211_ATTR_PROBE_RESP]);
2186 }
2187
Johannes Berg56d18932011-05-09 18:41:15 +02002188 err = call(&rdev->wiphy, dev, &params);
2189 if (!err && params.interval)
2190 wdev->beacon_interval = params.interval;
2191 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002192}
2193
2194static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info)
2195{
Johannes Berg4c476992010-10-04 21:36:35 +02002196 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2197 struct net_device *dev = info->user_ptr[1];
Johannes Berg56d18932011-05-09 18:41:15 +02002198 struct wireless_dev *wdev = dev->ieee80211_ptr;
2199 int err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002200
Johannes Berg4c476992010-10-04 21:36:35 +02002201 if (!rdev->ops->del_beacon)
2202 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002203
Johannes Berg074ac8d2010-09-16 14:58:22 +02002204 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02002205 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2206 return -EOPNOTSUPP;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002207
Johannes Berg56d18932011-05-09 18:41:15 +02002208 err = rdev->ops->del_beacon(&rdev->wiphy, dev);
2209 if (!err)
2210 wdev->beacon_interval = 0;
2211 return err;
Johannes Berged1b6cc2007-12-19 02:03:32 +01002212}
2213
Johannes Berg5727ef12007-12-19 02:03:34 +01002214static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = {
2215 [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG },
2216 [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG },
2217 [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG },
Jouni Malinen0e467242009-05-11 21:57:55 +03002218 [NL80211_STA_FLAG_MFP] = { .type = NLA_FLAG },
Javier Cardonab39c48f2011-04-07 15:08:30 -07002219 [NL80211_STA_FLAG_AUTHENTICATED] = { .type = NLA_FLAG },
Johannes Berg5727ef12007-12-19 02:03:34 +01002220};
2221
Johannes Bergeccb8e82009-05-11 21:57:56 +03002222static int parse_station_flags(struct genl_info *info,
2223 struct station_parameters *params)
Johannes Berg5727ef12007-12-19 02:03:34 +01002224{
2225 struct nlattr *flags[NL80211_STA_FLAG_MAX + 1];
Johannes Bergeccb8e82009-05-11 21:57:56 +03002226 struct nlattr *nla;
Johannes Berg5727ef12007-12-19 02:03:34 +01002227 int flag;
2228
Johannes Bergeccb8e82009-05-11 21:57:56 +03002229 /*
2230 * Try parsing the new attribute first so userspace
2231 * can specify both for older kernels.
2232 */
2233 nla = info->attrs[NL80211_ATTR_STA_FLAGS2];
2234 if (nla) {
2235 struct nl80211_sta_flag_update *sta_flags;
Johannes Berg5727ef12007-12-19 02:03:34 +01002236
Johannes Bergeccb8e82009-05-11 21:57:56 +03002237 sta_flags = nla_data(nla);
2238 params->sta_flags_mask = sta_flags->mask;
2239 params->sta_flags_set = sta_flags->set;
2240 if ((params->sta_flags_mask |
2241 params->sta_flags_set) & BIT(__NL80211_STA_FLAG_INVALID))
2242 return -EINVAL;
2243 return 0;
2244 }
2245
2246 /* if present, parse the old attribute */
2247
2248 nla = info->attrs[NL80211_ATTR_STA_FLAGS];
Johannes Berg5727ef12007-12-19 02:03:34 +01002249 if (!nla)
2250 return 0;
2251
2252 if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX,
2253 nla, sta_flags_policy))
2254 return -EINVAL;
2255
Johannes Bergeccb8e82009-05-11 21:57:56 +03002256 params->sta_flags_mask = (1 << __NL80211_STA_FLAG_AFTER_LAST) - 1;
2257 params->sta_flags_mask &= ~1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002258
2259 for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++)
2260 if (flags[flag])
Johannes Bergeccb8e82009-05-11 21:57:56 +03002261 params->sta_flags_set |= (1<<flag);
Johannes Berg5727ef12007-12-19 02:03:34 +01002262
2263 return 0;
2264}
2265
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002266static bool nl80211_put_sta_rate(struct sk_buff *msg, struct rate_info *info,
2267 int attr)
2268{
2269 struct nlattr *rate;
2270 u16 bitrate;
2271
2272 rate = nla_nest_start(msg, attr);
2273 if (!rate)
2274 goto nla_put_failure;
2275
2276 /* cfg80211_calculate_bitrate will return 0 for mcs >= 32 */
2277 bitrate = cfg80211_calculate_bitrate(info);
2278 if (bitrate > 0)
2279 NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate);
2280
2281 if (info->flags & RATE_INFO_FLAGS_MCS)
2282 NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, info->mcs);
2283 if (info->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH)
2284 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH);
2285 if (info->flags & RATE_INFO_FLAGS_SHORT_GI)
2286 NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI);
2287
2288 nla_nest_end(msg, rate);
2289 return true;
2290
2291nla_put_failure:
2292 return false;
2293}
2294
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002295static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
2296 int flags, struct net_device *dev,
Johannes Berg98b62182009-12-23 13:15:44 +01002297 const u8 *mac_addr, struct station_info *sinfo)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002298{
2299 void *hdr;
Paul Stewartf4263c92011-03-31 09:25:41 -07002300 struct nlattr *sinfoattr, *bss_param;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002301
2302 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2303 if (!hdr)
2304 return -1;
2305
2306 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2307 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
2308
Johannes Bergf5ea9122009-08-07 16:17:38 +02002309 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, sinfo->generation);
2310
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002311 sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
2312 if (!sinfoattr)
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002313 goto nla_put_failure;
Mohammed Shafi Shajakhanebe27c92011-04-08 21:24:24 +05302314 if (sinfo->filled & STATION_INFO_CONNECTED_TIME)
2315 NLA_PUT_U32(msg, NL80211_STA_INFO_CONNECTED_TIME,
2316 sinfo->connected_time);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002317 if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
2318 NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
2319 sinfo->inactive_time);
2320 if (sinfo->filled & STATION_INFO_RX_BYTES)
2321 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
2322 sinfo->rx_bytes);
2323 if (sinfo->filled & STATION_INFO_TX_BYTES)
2324 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
2325 sinfo->tx_bytes);
2326 if (sinfo->filled & STATION_INFO_LLID)
2327 NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
2328 sinfo->llid);
2329 if (sinfo->filled & STATION_INFO_PLID)
2330 NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
2331 sinfo->plid);
2332 if (sinfo->filled & STATION_INFO_PLINK_STATE)
2333 NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
2334 sinfo->plink_state);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002335 if (sinfo->filled & STATION_INFO_SIGNAL)
2336 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL,
2337 sinfo->signal);
Bruno Randolf541a45a2010-12-02 19:12:43 +09002338 if (sinfo->filled & STATION_INFO_SIGNAL_AVG)
2339 NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL_AVG,
2340 sinfo->signal_avg);
Henning Rogge420e7fa2008-12-11 22:04:19 +01002341 if (sinfo->filled & STATION_INFO_TX_BITRATE) {
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002342 if (!nl80211_put_sta_rate(msg, &sinfo->txrate,
2343 NL80211_STA_INFO_TX_BITRATE))
Henning Rogge420e7fa2008-12-11 22:04:19 +01002344 goto nla_put_failure;
Felix Fietkauc8dcfd82011-02-27 22:08:00 +01002345 }
2346 if (sinfo->filled & STATION_INFO_RX_BITRATE) {
2347 if (!nl80211_put_sta_rate(msg, &sinfo->rxrate,
2348 NL80211_STA_INFO_RX_BITRATE))
2349 goto nla_put_failure;
Henning Rogge420e7fa2008-12-11 22:04:19 +01002350 }
Jouni Malinen98c8a60a2009-02-17 13:24:57 +02002351 if (sinfo->filled & STATION_INFO_RX_PACKETS)
2352 NLA_PUT_U32(msg, NL80211_STA_INFO_RX_PACKETS,
2353 sinfo->rx_packets);
2354 if (sinfo->filled & STATION_INFO_TX_PACKETS)
2355 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_PACKETS,
2356 sinfo->tx_packets);
Bruno Randolfb206b4e2010-10-06 18:34:12 +09002357 if (sinfo->filled & STATION_INFO_TX_RETRIES)
2358 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_RETRIES,
2359 sinfo->tx_retries);
2360 if (sinfo->filled & STATION_INFO_TX_FAILED)
2361 NLA_PUT_U32(msg, NL80211_STA_INFO_TX_FAILED,
2362 sinfo->tx_failed);
Paul Stewartf4263c92011-03-31 09:25:41 -07002363 if (sinfo->filled & STATION_INFO_BSS_PARAM) {
2364 bss_param = nla_nest_start(msg, NL80211_STA_INFO_BSS_PARAM);
2365 if (!bss_param)
2366 goto nla_put_failure;
2367
2368 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_CTS_PROT)
2369 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_CTS_PROT);
2370 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_PREAMBLE)
2371 NLA_PUT_FLAG(msg, NL80211_STA_BSS_PARAM_SHORT_PREAMBLE);
2372 if (sinfo->bss_param.flags & BSS_PARAM_FLAGS_SHORT_SLOT_TIME)
2373 NLA_PUT_FLAG(msg,
2374 NL80211_STA_BSS_PARAM_SHORT_SLOT_TIME);
2375 NLA_PUT_U8(msg, NL80211_STA_BSS_PARAM_DTIM_PERIOD,
2376 sinfo->bss_param.dtim_period);
2377 NLA_PUT_U16(msg, NL80211_STA_BSS_PARAM_BEACON_INTERVAL,
2378 sinfo->bss_param.beacon_interval);
2379
2380 nla_nest_end(msg, bss_param);
2381 }
Helmut Schaabb6e7532011-10-13 16:30:39 +02002382 if (sinfo->filled & STATION_INFO_STA_FLAGS)
2383 NLA_PUT(msg, NL80211_STA_INFO_STA_FLAGS,
2384 sizeof(struct nl80211_sta_flag_update),
2385 &sinfo->sta_flags);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002386 nla_nest_end(msg, sinfoattr);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002387
Felix Fietkau040bdf72011-08-10 19:00:33 -06002388 if (sinfo->filled & STATION_INFO_ASSOC_REQ_IES)
Jouni Malinen50d3dfb2011-08-08 12:11:52 +03002389 NLA_PUT(msg, NL80211_ATTR_IE, sinfo->assoc_req_ies_len,
2390 sinfo->assoc_req_ies);
2391
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002392 return genlmsg_end(msg, hdr);
2393
2394 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002395 genlmsg_cancel(msg, hdr);
2396 return -EMSGSIZE;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002397}
2398
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002399static int nl80211_dump_station(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002400 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002401{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002402 struct station_info sinfo;
2403 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002404 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002405 u8 mac_addr[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002406 int sta_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002407 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002408
Johannes Berg67748892010-10-04 21:14:06 +02002409 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2410 if (err)
2411 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002412
2413 if (!dev->ops->dump_station) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002414 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002415 goto out_err;
2416 }
2417
Johannes Bergbba95fe2008-07-29 13:22:51 +02002418 while (1) {
Jouni Malinenf612ced2011-08-11 11:46:22 +03002419 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergbba95fe2008-07-29 13:22:51 +02002420 err = dev->ops->dump_station(&dev->wiphy, netdev, sta_idx,
2421 mac_addr, &sinfo);
2422 if (err == -ENOENT)
2423 break;
2424 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002425 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002426
2427 if (nl80211_send_station(skb,
2428 NETLINK_CB(cb->skb).pid,
2429 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2430 netdev, mac_addr,
2431 &sinfo) < 0)
2432 goto out;
2433
2434 sta_idx++;
2435 }
2436
2437
2438 out:
2439 cb->args[1] = sta_idx;
2440 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002441 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002442 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002443
2444 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002445}
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002446
Johannes Berg5727ef12007-12-19 02:03:34 +01002447static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
2448{
Johannes Berg4c476992010-10-04 21:36:35 +02002449 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2450 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002451 struct station_info sinfo;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002452 struct sk_buff *msg;
2453 u8 *mac_addr = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02002454 int err;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002455
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002456 memset(&sinfo, 0, sizeof(sinfo));
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002457
2458 if (!info->attrs[NL80211_ATTR_MAC])
2459 return -EINVAL;
2460
2461 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2462
Johannes Berg4c476992010-10-04 21:36:35 +02002463 if (!rdev->ops->get_station)
2464 return -EOPNOTSUPP;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002465
Johannes Berg79c97e92009-07-07 03:56:12 +02002466 err = rdev->ops->get_station(&rdev->wiphy, dev, mac_addr, &sinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002467 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002468 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002469
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002470 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002471 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002472 return -ENOMEM;
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002473
2474 if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002475 dev, mac_addr, &sinfo) < 0) {
2476 nlmsg_free(msg);
2477 return -ENOBUFS;
2478 }
Johannes Bergfd5b74d2007-12-19 02:03:36 +01002479
Johannes Berg4c476992010-10-04 21:36:35 +02002480 return genlmsg_reply(msg, info);
Johannes Berg5727ef12007-12-19 02:03:34 +01002481}
2482
2483/*
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002484 * Get vlan interface making sure it is running and on the right wiphy.
Johannes Berg5727ef12007-12-19 02:03:34 +01002485 */
Johannes Berg463d0182009-07-14 00:33:35 +02002486static int get_vlan(struct genl_info *info,
Johannes Berg5727ef12007-12-19 02:03:34 +01002487 struct cfg80211_registered_device *rdev,
2488 struct net_device **vlan)
2489{
Johannes Berg463d0182009-07-14 00:33:35 +02002490 struct nlattr *vlanattr = info->attrs[NL80211_ATTR_STA_VLAN];
Johannes Berg5727ef12007-12-19 02:03:34 +01002491 *vlan = NULL;
2492
2493 if (vlanattr) {
Johannes Berg463d0182009-07-14 00:33:35 +02002494 *vlan = dev_get_by_index(genl_info_net(info),
2495 nla_get_u32(vlanattr));
Johannes Berg5727ef12007-12-19 02:03:34 +01002496 if (!*vlan)
2497 return -ENODEV;
2498 if (!(*vlan)->ieee80211_ptr)
2499 return -EINVAL;
2500 if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy)
2501 return -EINVAL;
Felix Fietkauc258d2d2009-11-11 17:23:31 +01002502 if (!netif_running(*vlan))
2503 return -ENETDOWN;
Johannes Berg5727ef12007-12-19 02:03:34 +01002504 }
2505 return 0;
2506}
2507
2508static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
2509{
Johannes Berg4c476992010-10-04 21:36:35 +02002510 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002511 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002512 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002513 struct station_parameters params;
2514 u8 *mac_addr = NULL;
2515
2516 memset(&params, 0, sizeof(params));
2517
2518 params.listen_interval = -1;
Javier Cardona57cf8042011-05-13 10:45:43 -07002519 params.plink_state = -1;
Johannes Berg5727ef12007-12-19 02:03:34 +01002520
2521 if (info->attrs[NL80211_ATTR_STA_AID])
2522 return -EINVAL;
2523
2524 if (!info->attrs[NL80211_ATTR_MAC])
2525 return -EINVAL;
2526
2527 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2528
2529 if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) {
2530 params.supported_rates =
2531 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2532 params.supported_rates_len =
2533 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2534 }
2535
2536 if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2537 params.listen_interval =
2538 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
2539
Jouni Malinen36aedc902008-08-25 11:58:58 +03002540 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2541 params.ht_capa =
2542 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
2543
Johannes Bergeccb8e82009-05-11 21:57:56 +03002544 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002545 return -EINVAL;
2546
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002547 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2548 params.plink_action =
2549 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2550
Javier Cardona9c3990a2011-05-03 16:57:11 -07002551 if (info->attrs[NL80211_ATTR_STA_PLINK_STATE])
2552 params.plink_state =
2553 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_STATE]);
2554
Johannes Berg463d0182009-07-14 00:33:35 +02002555 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002556 if (err)
Johannes Berg034d6552009-05-27 10:35:29 +02002557 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002558
2559 /* validate settings */
2560 err = 0;
2561
2562 switch (dev->ieee80211_ptr->iftype) {
2563 case NL80211_IFTYPE_AP:
2564 case NL80211_IFTYPE_AP_VLAN:
Johannes Berg074ac8d2010-09-16 14:58:22 +02002565 case NL80211_IFTYPE_P2P_GO:
Johannes Berga97f4422009-06-18 17:23:43 +02002566 /* disallow mesh-specific things */
2567 if (params.plink_action)
2568 err = -EINVAL;
2569 break;
Johannes Berg074ac8d2010-09-16 14:58:22 +02002570 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berga97f4422009-06-18 17:23:43 +02002571 case NL80211_IFTYPE_STATION:
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002572 /* disallow things sta doesn't support */
Johannes Berga97f4422009-06-18 17:23:43 +02002573 if (params.plink_action)
2574 err = -EINVAL;
2575 if (params.vlan)
2576 err = -EINVAL;
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002577 if (params.supported_rates &&
2578 !(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)))
Johannes Berga97f4422009-06-18 17:23:43 +02002579 err = -EINVAL;
2580 if (params.ht_capa)
2581 err = -EINVAL;
2582 if (params.listen_interval >= 0)
2583 err = -EINVAL;
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002584 if (params.sta_flags_mask &
2585 ~(BIT(NL80211_STA_FLAG_AUTHORIZED) |
2586 BIT(NL80211_STA_FLAG_TDLS_PEER)))
2587 err = -EINVAL;
2588 /* can't change the TDLS bit */
2589 if (!(params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
2590 (params.sta_flags_mask & BIT(NL80211_STA_FLAG_TDLS_PEER)))
Johannes Berga97f4422009-06-18 17:23:43 +02002591 err = -EINVAL;
2592 break;
2593 case NL80211_IFTYPE_MESH_POINT:
2594 /* disallow things mesh doesn't support */
2595 if (params.vlan)
2596 err = -EINVAL;
2597 if (params.ht_capa)
2598 err = -EINVAL;
2599 if (params.listen_interval >= 0)
2600 err = -EINVAL;
Javier Cardonab39c48f2011-04-07 15:08:30 -07002601 if (params.sta_flags_mask &
2602 ~(BIT(NL80211_STA_FLAG_AUTHENTICATED) |
Thomas Pedersen84298282011-05-03 16:57:13 -07002603 BIT(NL80211_STA_FLAG_MFP) |
Javier Cardonab39c48f2011-04-07 15:08:30 -07002604 BIT(NL80211_STA_FLAG_AUTHORIZED)))
Johannes Berga97f4422009-06-18 17:23:43 +02002605 err = -EINVAL;
2606 break;
2607 default:
2608 err = -EINVAL;
Johannes Berg034d6552009-05-27 10:35:29 +02002609 }
2610
Johannes Berg5727ef12007-12-19 02:03:34 +01002611 if (err)
2612 goto out;
2613
Johannes Berg79c97e92009-07-07 03:56:12 +02002614 if (!rdev->ops->change_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002615 err = -EOPNOTSUPP;
2616 goto out;
2617 }
2618
Johannes Berg79c97e92009-07-07 03:56:12 +02002619 err = rdev->ops->change_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002620
2621 out:
2622 if (params.vlan)
2623 dev_put(params.vlan);
Johannes Berg3b858752009-03-12 09:55:09 +01002624
Johannes Berg5727ef12007-12-19 02:03:34 +01002625 return err;
2626}
2627
Eliad Pellerc75786c2011-08-23 14:37:46 +03002628static struct nla_policy
2629nl80211_sta_wme_policy[NL80211_STA_WME_MAX + 1] __read_mostly = {
2630 [NL80211_STA_WME_UAPSD_QUEUES] = { .type = NLA_U8 },
2631 [NL80211_STA_WME_MAX_SP] = { .type = NLA_U8 },
2632};
2633
Johannes Berg5727ef12007-12-19 02:03:34 +01002634static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info)
2635{
Johannes Berg4c476992010-10-04 21:36:35 +02002636 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg5727ef12007-12-19 02:03:34 +01002637 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002638 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002639 struct station_parameters params;
2640 u8 *mac_addr = NULL;
2641
2642 memset(&params, 0, sizeof(params));
2643
2644 if (!info->attrs[NL80211_ATTR_MAC])
2645 return -EINVAL;
2646
Johannes Berg5727ef12007-12-19 02:03:34 +01002647 if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL])
2648 return -EINVAL;
2649
2650 if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES])
2651 return -EINVAL;
2652
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002653 if (!info->attrs[NL80211_ATTR_STA_AID])
2654 return -EINVAL;
2655
Johannes Berg5727ef12007-12-19 02:03:34 +01002656 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2657 params.supported_rates =
2658 nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2659 params.supported_rates_len =
2660 nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]);
2661 params.listen_interval =
2662 nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]);
Johannes Berg51b50fb2009-05-24 16:42:30 +02002663
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002664 params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]);
2665 if (!params.aid || params.aid > IEEE80211_MAX_AID)
2666 return -EINVAL;
Johannes Berg51b50fb2009-05-24 16:42:30 +02002667
Jouni Malinen36aedc902008-08-25 11:58:58 +03002668 if (info->attrs[NL80211_ATTR_HT_CAPABILITY])
2669 params.ht_capa =
2670 nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]);
Johannes Berg5727ef12007-12-19 02:03:34 +01002671
Javier Cardona96b78df2011-04-07 15:08:33 -07002672 if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
2673 params.plink_action =
2674 nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
2675
Johannes Bergeccb8e82009-05-11 21:57:56 +03002676 if (parse_station_flags(info, &params))
Johannes Berg5727ef12007-12-19 02:03:34 +01002677 return -EINVAL;
2678
Eliad Pellerc75786c2011-08-23 14:37:46 +03002679 /* parse WME attributes if sta is WME capable */
Eliad Pellercedb5412011-08-31 11:29:43 +03002680 if ((rdev->wiphy.flags & WIPHY_FLAG_AP_UAPSD) &&
Arik Nemtsovcd329842011-09-26 09:36:42 +03002681 (params.sta_flags_set & BIT(NL80211_STA_FLAG_WME)) &&
Eliad Pellerc75786c2011-08-23 14:37:46 +03002682 info->attrs[NL80211_ATTR_STA_WME]) {
2683 struct nlattr *tb[NL80211_STA_WME_MAX + 1];
2684 struct nlattr *nla;
2685
2686 nla = info->attrs[NL80211_ATTR_STA_WME];
2687 err = nla_parse_nested(tb, NL80211_STA_WME_MAX, nla,
2688 nl80211_sta_wme_policy);
2689 if (err)
2690 return err;
2691
2692 if (tb[NL80211_STA_WME_UAPSD_QUEUES])
2693 params.uapsd_queues =
2694 nla_get_u8(tb[NL80211_STA_WME_UAPSD_QUEUES]);
Johannes Berg4319e192011-09-07 11:50:48 +02002695 if (params.uapsd_queues & ~IEEE80211_WMM_IE_STA_QOSINFO_AC_MASK)
2696 return -EINVAL;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002697
2698 if (tb[NL80211_STA_WME_MAX_SP])
2699 params.max_sp =
2700 nla_get_u8(tb[NL80211_STA_WME_MAX_SP]);
Johannes Berg4319e192011-09-07 11:50:48 +02002701
2702 if (params.max_sp & ~IEEE80211_WMM_IE_STA_QOSINFO_SP_MASK)
2703 return -EINVAL;
Johannes Berg3b9ce802011-09-27 20:56:12 +02002704
2705 params.sta_modify_mask |= STATION_PARAM_APPLY_UAPSD;
Eliad Pellerc75786c2011-08-23 14:37:46 +03002706 }
2707
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002708 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002709 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardona96b78df2011-04-07 15:08:33 -07002710 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Arik Nemtsov07ba55d2011-09-28 14:12:53 +03002711 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO &&
2712 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION)
2713 return -EINVAL;
2714
2715 /*
2716 * Only managed stations can add TDLS peers, and only when the
2717 * wiphy supports external TDLS setup.
2718 */
2719 if (dev->ieee80211_ptr->iftype == NL80211_IFTYPE_STATION &&
2720 !((params.sta_flags_set & BIT(NL80211_STA_FLAG_TDLS_PEER)) &&
2721 (rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) &&
2722 (rdev->wiphy.flags & WIPHY_FLAG_TDLS_EXTERNAL_SETUP)))
Johannes Berg4c476992010-10-04 21:36:35 +02002723 return -EINVAL;
Thadeu Lima de Souza Cascardo0e956c12010-02-12 12:34:50 -02002724
Johannes Berg463d0182009-07-14 00:33:35 +02002725 err = get_vlan(info, rdev, &params.vlan);
Johannes Berga97f4422009-06-18 17:23:43 +02002726 if (err)
Johannes Berge80cf852009-05-11 14:43:13 +02002727 goto out;
Johannes Berga97f4422009-06-18 17:23:43 +02002728
2729 /* validate settings */
2730 err = 0;
2731
Johannes Berg79c97e92009-07-07 03:56:12 +02002732 if (!rdev->ops->add_station) {
Johannes Berg5727ef12007-12-19 02:03:34 +01002733 err = -EOPNOTSUPP;
2734 goto out;
2735 }
2736
Johannes Berg79c97e92009-07-07 03:56:12 +02002737 err = rdev->ops->add_station(&rdev->wiphy, dev, mac_addr, &params);
Johannes Berg5727ef12007-12-19 02:03:34 +01002738
2739 out:
2740 if (params.vlan)
2741 dev_put(params.vlan);
Johannes Berg5727ef12007-12-19 02:03:34 +01002742 return err;
2743}
2744
2745static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
2746{
Johannes Berg4c476992010-10-04 21:36:35 +02002747 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2748 struct net_device *dev = info->user_ptr[1];
Johannes Berg5727ef12007-12-19 02:03:34 +01002749 u8 *mac_addr = NULL;
2750
2751 if (info->attrs[NL80211_ATTR_MAC])
2752 mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
2753
Johannes Berge80cf852009-05-11 14:43:13 +02002754 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Marco Porschd5d9de02010-03-30 10:00:16 +02002755 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02002756 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02002757 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
2758 return -EINVAL;
Johannes Berge80cf852009-05-11 14:43:13 +02002759
Johannes Berg4c476992010-10-04 21:36:35 +02002760 if (!rdev->ops->del_station)
2761 return -EOPNOTSUPP;
Johannes Berg5727ef12007-12-19 02:03:34 +01002762
Johannes Berg4c476992010-10-04 21:36:35 +02002763 return rdev->ops->del_station(&rdev->wiphy, dev, mac_addr);
Johannes Berg5727ef12007-12-19 02:03:34 +01002764}
2765
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002766static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
2767 int flags, struct net_device *dev,
2768 u8 *dst, u8 *next_hop,
2769 struct mpath_info *pinfo)
2770{
2771 void *hdr;
2772 struct nlattr *pinfoattr;
2773
2774 hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
2775 if (!hdr)
2776 return -1;
2777
2778 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
2779 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
2780 NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
2781
Johannes Bergf5ea9122009-08-07 16:17:38 +02002782 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, pinfo->generation);
2783
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002784 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
2785 if (!pinfoattr)
2786 goto nla_put_failure;
2787 if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
2788 NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
2789 pinfo->frame_qlen);
Rui Paulod19b3bf2009-11-09 23:46:55 +00002790 if (pinfo->filled & MPATH_INFO_SN)
2791 NLA_PUT_U32(msg, NL80211_MPATH_INFO_SN,
2792 pinfo->sn);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002793 if (pinfo->filled & MPATH_INFO_METRIC)
2794 NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
2795 pinfo->metric);
2796 if (pinfo->filled & MPATH_INFO_EXPTIME)
2797 NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
2798 pinfo->exptime);
2799 if (pinfo->filled & MPATH_INFO_FLAGS)
2800 NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
2801 pinfo->flags);
2802 if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
2803 NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
2804 pinfo->discovery_timeout);
2805 if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
2806 NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
2807 pinfo->discovery_retries);
2808
2809 nla_nest_end(msg, pinfoattr);
2810
2811 return genlmsg_end(msg, hdr);
2812
2813 nla_put_failure:
Thomas Grafbc3ed282008-06-03 16:36:54 -07002814 genlmsg_cancel(msg, hdr);
2815 return -EMSGSIZE;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002816}
2817
2818static int nl80211_dump_mpath(struct sk_buff *skb,
Johannes Bergbba95fe2008-07-29 13:22:51 +02002819 struct netlink_callback *cb)
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002820{
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002821 struct mpath_info pinfo;
2822 struct cfg80211_registered_device *dev;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002823 struct net_device *netdev;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002824 u8 dst[ETH_ALEN];
2825 u8 next_hop[ETH_ALEN];
Johannes Bergbba95fe2008-07-29 13:22:51 +02002826 int path_idx = cb->args[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002827 int err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002828
Johannes Berg67748892010-10-04 21:14:06 +02002829 err = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
2830 if (err)
2831 return err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002832
2833 if (!dev->ops->dump_mpath) {
Jouni Malineneec60b02009-03-20 21:21:19 +02002834 err = -EOPNOTSUPP;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002835 goto out_err;
2836 }
2837
Jouni Malineneec60b02009-03-20 21:21:19 +02002838 if (netdev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT) {
2839 err = -EOPNOTSUPP;
Roel Kluin0448b5f2009-08-22 21:15:49 +02002840 goto out_err;
Jouni Malineneec60b02009-03-20 21:21:19 +02002841 }
2842
Johannes Bergbba95fe2008-07-29 13:22:51 +02002843 while (1) {
2844 err = dev->ops->dump_mpath(&dev->wiphy, netdev, path_idx,
2845 dst, next_hop, &pinfo);
2846 if (err == -ENOENT)
2847 break;
2848 if (err)
Johannes Berg3b858752009-03-12 09:55:09 +01002849 goto out_err;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002850
2851 if (nl80211_send_mpath(skb, NETLINK_CB(cb->skb).pid,
2852 cb->nlh->nlmsg_seq, NLM_F_MULTI,
2853 netdev, dst, next_hop,
2854 &pinfo) < 0)
2855 goto out;
2856
2857 path_idx++;
2858 }
2859
2860
2861 out:
2862 cb->args[1] = path_idx;
2863 err = skb->len;
Johannes Bergbba95fe2008-07-29 13:22:51 +02002864 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02002865 nl80211_finish_netdev_dump(dev);
Johannes Bergbba95fe2008-07-29 13:22:51 +02002866 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002867}
2868
2869static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
2870{
Johannes Berg4c476992010-10-04 21:36:35 +02002871 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002872 int err;
Johannes Berg4c476992010-10-04 21:36:35 +02002873 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002874 struct mpath_info pinfo;
2875 struct sk_buff *msg;
2876 u8 *dst = NULL;
2877 u8 next_hop[ETH_ALEN];
2878
2879 memset(&pinfo, 0, sizeof(pinfo));
2880
2881 if (!info->attrs[NL80211_ATTR_MAC])
2882 return -EINVAL;
2883
2884 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2885
Johannes Berg4c476992010-10-04 21:36:35 +02002886 if (!rdev->ops->get_mpath)
2887 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002888
Johannes Berg4c476992010-10-04 21:36:35 +02002889 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2890 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02002891
Johannes Berg79c97e92009-07-07 03:56:12 +02002892 err = rdev->ops->get_mpath(&rdev->wiphy, dev, dst, next_hop, &pinfo);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002893 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02002894 return err;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002895
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07002896 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002897 if (!msg)
Johannes Berg4c476992010-10-04 21:36:35 +02002898 return -ENOMEM;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002899
2900 if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
Johannes Berg4c476992010-10-04 21:36:35 +02002901 dev, dst, next_hop, &pinfo) < 0) {
2902 nlmsg_free(msg);
2903 return -ENOBUFS;
2904 }
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002905
Johannes Berg4c476992010-10-04 21:36:35 +02002906 return genlmsg_reply(msg, info);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002907}
2908
2909static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
2910{
Johannes Berg4c476992010-10-04 21:36:35 +02002911 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2912 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002913 u8 *dst = NULL;
2914 u8 *next_hop = NULL;
2915
2916 if (!info->attrs[NL80211_ATTR_MAC])
2917 return -EINVAL;
2918
2919 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2920 return -EINVAL;
2921
2922 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2923 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2924
Johannes Berg4c476992010-10-04 21:36:35 +02002925 if (!rdev->ops->change_mpath)
2926 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002927
Johannes Berg4c476992010-10-04 21:36:35 +02002928 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2929 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002930
Johannes Berg4c476992010-10-04 21:36:35 +02002931 return rdev->ops->change_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002932}
Johannes Berg4c476992010-10-04 21:36:35 +02002933
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002934static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
2935{
Johannes Berg4c476992010-10-04 21:36:35 +02002936 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2937 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002938 u8 *dst = NULL;
2939 u8 *next_hop = NULL;
2940
2941 if (!info->attrs[NL80211_ATTR_MAC])
2942 return -EINVAL;
2943
2944 if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
2945 return -EINVAL;
2946
2947 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2948 next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
2949
Johannes Berg4c476992010-10-04 21:36:35 +02002950 if (!rdev->ops->add_mpath)
2951 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002952
Johannes Berg4c476992010-10-04 21:36:35 +02002953 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT)
2954 return -EOPNOTSUPP;
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002955
Johannes Berg4c476992010-10-04 21:36:35 +02002956 return rdev->ops->add_mpath(&rdev->wiphy, dev, dst, next_hop);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002957}
2958
2959static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
2960{
Johannes Berg4c476992010-10-04 21:36:35 +02002961 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2962 struct net_device *dev = info->user_ptr[1];
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002963 u8 *dst = NULL;
2964
2965 if (info->attrs[NL80211_ATTR_MAC])
2966 dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
2967
Johannes Berg4c476992010-10-04 21:36:35 +02002968 if (!rdev->ops->del_mpath)
2969 return -EOPNOTSUPP;
Johannes Berg3b858752009-03-12 09:55:09 +01002970
Johannes Berg4c476992010-10-04 21:36:35 +02002971 return rdev->ops->del_mpath(&rdev->wiphy, dev, dst);
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01002972}
2973
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002974static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info)
2975{
Johannes Berg4c476992010-10-04 21:36:35 +02002976 struct cfg80211_registered_device *rdev = info->user_ptr[0];
2977 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002978 struct bss_parameters params;
2979
2980 memset(&params, 0, sizeof(params));
2981 /* default to not changing parameters */
2982 params.use_cts_prot = -1;
2983 params.use_short_preamble = -1;
2984 params.use_short_slot_time = -1;
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02002985 params.ap_isolate = -1;
Helmut Schaa50b12f52010-11-19 12:40:25 +01002986 params.ht_opmode = -1;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03002987
2988 if (info->attrs[NL80211_ATTR_BSS_CTS_PROT])
2989 params.use_cts_prot =
2990 nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]);
2991 if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE])
2992 params.use_short_preamble =
2993 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]);
2994 if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME])
2995 params.use_short_slot_time =
2996 nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]);
Jouni Malinen90c97a02008-10-30 16:59:22 +02002997 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
2998 params.basic_rates =
2999 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3000 params.basic_rates_len =
3001 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
3002 }
Felix Fietkaufd8aaaf2010-04-27 01:23:35 +02003003 if (info->attrs[NL80211_ATTR_AP_ISOLATE])
3004 params.ap_isolate = !!nla_get_u8(info->attrs[NL80211_ATTR_AP_ISOLATE]);
Helmut Schaa50b12f52010-11-19 12:40:25 +01003005 if (info->attrs[NL80211_ATTR_BSS_HT_OPMODE])
3006 params.ht_opmode =
3007 nla_get_u16(info->attrs[NL80211_ATTR_BSS_HT_OPMODE]);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003008
Johannes Berg4c476992010-10-04 21:36:35 +02003009 if (!rdev->ops->change_bss)
3010 return -EOPNOTSUPP;
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003011
Johannes Berg074ac8d2010-09-16 14:58:22 +02003012 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
Johannes Berg4c476992010-10-04 21:36:35 +02003013 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
3014 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02003015
Johannes Berg4c476992010-10-04 21:36:35 +02003016 return rdev->ops->change_bss(&rdev->wiphy, dev, &params);
Jouni Malinen9f1ba902008-08-07 20:07:01 +03003017}
3018
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003019static const struct nla_policy reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003020 [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 },
3021 [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 },
3022 [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 },
3023 [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 },
3024 [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 },
3025 [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 },
3026};
3027
3028static int parse_reg_rule(struct nlattr *tb[],
3029 struct ieee80211_reg_rule *reg_rule)
3030{
3031 struct ieee80211_freq_range *freq_range = &reg_rule->freq_range;
3032 struct ieee80211_power_rule *power_rule = &reg_rule->power_rule;
3033
3034 if (!tb[NL80211_ATTR_REG_RULE_FLAGS])
3035 return -EINVAL;
3036 if (!tb[NL80211_ATTR_FREQ_RANGE_START])
3037 return -EINVAL;
3038 if (!tb[NL80211_ATTR_FREQ_RANGE_END])
3039 return -EINVAL;
3040 if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW])
3041 return -EINVAL;
3042 if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP])
3043 return -EINVAL;
3044
3045 reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]);
3046
3047 freq_range->start_freq_khz =
3048 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]);
3049 freq_range->end_freq_khz =
3050 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]);
3051 freq_range->max_bandwidth_khz =
3052 nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]);
3053
3054 power_rule->max_eirp =
3055 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]);
3056
3057 if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN])
3058 power_rule->max_antenna_gain =
3059 nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]);
3060
3061 return 0;
3062}
3063
3064static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info)
3065{
3066 int r;
3067 char *data = NULL;
3068
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003069 /*
3070 * You should only get this when cfg80211 hasn't yet initialized
3071 * completely when built-in to the kernel right between the time
3072 * window between nl80211_init() and regulatory_init(), if that is
3073 * even possible.
3074 */
3075 mutex_lock(&cfg80211_mutex);
3076 if (unlikely(!cfg80211_regdomain)) {
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003077 mutex_unlock(&cfg80211_mutex);
3078 return -EINPROGRESS;
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003079 }
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003080 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguez80778f12009-02-21 00:04:22 -05003081
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003082 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3083 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003084
3085 data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3086
Luis R. Rodriguezfe33eb32009-02-21 00:04:30 -05003087 r = regulatory_hint_user(data);
3088
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003089 return r;
3090}
3091
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003092static int nl80211_get_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003093 struct genl_info *info)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003094{
Johannes Berg4c476992010-10-04 21:36:35 +02003095 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg4c476992010-10-04 21:36:35 +02003096 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003097 struct wireless_dev *wdev = dev->ieee80211_ptr;
3098 struct mesh_config cur_params;
3099 int err = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003100 void *hdr;
3101 struct nlattr *pinfoattr;
3102 struct sk_buff *msg;
3103
Johannes Berg29cbe682010-12-03 09:20:44 +01003104 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3105 return -EOPNOTSUPP;
3106
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003107 if (!rdev->ops->get_mesh_config)
Johannes Berg4c476992010-10-04 21:36:35 +02003108 return -EOPNOTSUPP;
Jouni Malinenf3f92582009-03-20 17:57:36 +02003109
Johannes Berg29cbe682010-12-03 09:20:44 +01003110 wdev_lock(wdev);
3111 /* If not connected, get default parameters */
3112 if (!wdev->mesh_id_len)
3113 memcpy(&cur_params, &default_mesh_config, sizeof(cur_params));
3114 else
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003115 err = rdev->ops->get_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003116 &cur_params);
3117 wdev_unlock(wdev);
3118
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003119 if (err)
Johannes Berg4c476992010-10-04 21:36:35 +02003120 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003121
3122 /* Draw up a netlink message to send back */
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003123 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003124 if (!msg)
3125 return -ENOMEM;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003126 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003127 NL80211_CMD_GET_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003128 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003129 goto out;
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003130 pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_CONFIG);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003131 if (!pinfoattr)
3132 goto nla_put_failure;
3133 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
3134 NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT,
3135 cur_params.dot11MeshRetryTimeout);
3136 NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT,
3137 cur_params.dot11MeshConfirmTimeout);
3138 NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT,
3139 cur_params.dot11MeshHoldingTimeout);
3140 NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS,
3141 cur_params.dot11MeshMaxPeerLinks);
3142 NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES,
3143 cur_params.dot11MeshMaxRetries);
3144 NLA_PUT_U8(msg, NL80211_MESHCONF_TTL,
3145 cur_params.dot11MeshTTL);
Javier Cardona45904f22010-12-03 09:20:40 +01003146 NLA_PUT_U8(msg, NL80211_MESHCONF_ELEMENT_TTL,
3147 cur_params.element_ttl);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003148 NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS,
3149 cur_params.auto_open_plinks);
3150 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3151 cur_params.dot11MeshHWMPmaxPREQretries);
3152 NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME,
3153 cur_params.path_refresh_time);
3154 NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3155 cur_params.min_discovery_timeout);
3156 NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3157 cur_params.dot11MeshHWMPactivePathTimeout);
3158 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3159 cur_params.dot11MeshHWMPpreqMinInterval);
3160 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3161 cur_params.dot11MeshHWMPnetDiameterTraversalTime);
Rui Paulo63c57232009-11-09 23:46:57 +00003162 NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_ROOTMODE,
3163 cur_params.dot11MeshHWMPRootMode);
Javier Cardona0507e152011-08-09 16:45:10 -07003164 NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3165 cur_params.dot11MeshHWMPRannInterval);
Javier Cardona16dd7262011-08-09 16:45:11 -07003166 NLA_PUT_U8(msg, NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3167 cur_params.dot11MeshGateAnnouncementProtocol);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003168 nla_nest_end(msg, pinfoattr);
3169 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02003170 return genlmsg_reply(msg, info);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003171
Johannes Berg3b858752009-03-12 09:55:09 +01003172 nla_put_failure:
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003173 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003174 out:
Yuri Ershovd080e272010-06-29 15:08:07 +04003175 nlmsg_free(msg);
Johannes Berg4c476992010-10-04 21:36:35 +02003176 return -ENOBUFS;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003177}
3178
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00003179static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] = {
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003180 [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 },
3181 [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 },
3182 [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 },
3183 [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 },
3184 [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 },
3185 [NL80211_MESHCONF_TTL] = { .type = NLA_U8 },
Javier Cardona45904f22010-12-03 09:20:40 +01003186 [NL80211_MESHCONF_ELEMENT_TTL] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003187 [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 },
3188
3189 [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 },
3190 [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 },
3191 [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 },
3192 [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
3193 [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
3194 [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
Javier Cardona699403d2011-08-09 16:45:09 -07003195 [NL80211_MESHCONF_HWMP_ROOTMODE] = { .type = NLA_U8 },
Javier Cardona0507e152011-08-09 16:45:10 -07003196 [NL80211_MESHCONF_HWMP_RANN_INTERVAL] = { .type = NLA_U16 },
Javier Cardona16dd7262011-08-09 16:45:11 -07003197 [NL80211_MESHCONF_GATE_ANNOUNCEMENTS] = { .type = NLA_U8 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003198};
3199
Javier Cardonac80d5452010-12-16 17:37:49 -08003200static const struct nla_policy
3201 nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
3202 [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
3203 [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
Javier Cardona15d5dda2011-04-07 15:08:28 -07003204 [NL80211_MESH_SETUP_USERSPACE_AUTH] = { .type = NLA_FLAG },
Javier Cardona581a8b02011-04-07 15:08:27 -07003205 [NL80211_MESH_SETUP_IE] = { .type = NLA_BINARY,
Javier Cardonac80d5452010-12-16 17:37:49 -08003206 .len = IEEE80211_MAX_DATA_LEN },
Javier Cardonab130e5c2011-05-03 16:57:07 -07003207 [NL80211_MESH_SETUP_USERSPACE_AMPE] = { .type = NLA_FLAG },
Javier Cardonac80d5452010-12-16 17:37:49 -08003208};
3209
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003210static int nl80211_parse_mesh_config(struct genl_info *info,
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003211 struct mesh_config *cfg,
3212 u32 *mask_out)
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003213{
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003214 struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1];
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003215 u32 mask = 0;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003216
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003217#define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \
3218do {\
3219 if (table[attr_num]) {\
3220 cfg->param = nla_fn(table[attr_num]); \
3221 mask |= (1 << (attr_num - 1)); \
3222 } \
3223} while (0);\
3224
3225
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003226 if (!info->attrs[NL80211_ATTR_MESH_CONFIG])
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003227 return -EINVAL;
3228 if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX,
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003229 info->attrs[NL80211_ATTR_MESH_CONFIG],
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003230 nl80211_meshconf_params_policy))
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003231 return -EINVAL;
3232
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003233 /* This makes sure that there aren't more than 32 mesh config
3234 * parameters (otherwise our bitfield scheme would not work.) */
3235 BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32);
3236
3237 /* Fill in the params struct */
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003238 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout,
3239 mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16);
3240 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout,
3241 mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16);
3242 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout,
3243 mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16);
3244 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks,
3245 mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16);
3246 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries,
3247 mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8);
3248 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL,
3249 mask, NL80211_MESHCONF_TTL, nla_get_u8);
Javier Cardona45904f22010-12-03 09:20:40 +01003250 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, element_ttl,
3251 mask, NL80211_MESHCONF_ELEMENT_TTL, nla_get_u8);
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003252 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks,
3253 mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8);
3254 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries,
3255 mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES,
3256 nla_get_u8);
3257 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time,
3258 mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32);
3259 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout,
3260 mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT,
3261 nla_get_u16);
3262 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout,
3263 mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
3264 nla_get_u32);
3265 FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval,
3266 mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
3267 nla_get_u16);
3268 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3269 dot11MeshHWMPnetDiameterTraversalTime,
3270 mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
3271 nla_get_u16);
Rui Paulo63c57232009-11-09 23:46:57 +00003272 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3273 dot11MeshHWMPRootMode, mask,
3274 NL80211_MESHCONF_HWMP_ROOTMODE,
3275 nla_get_u8);
Javier Cardona0507e152011-08-09 16:45:10 -07003276 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3277 dot11MeshHWMPRannInterval, mask,
3278 NL80211_MESHCONF_HWMP_RANN_INTERVAL,
3279 nla_get_u16);
Javier Cardona16dd7262011-08-09 16:45:11 -07003280 FILL_IN_MESH_PARAM_IF_SET(tb, cfg,
3281 dot11MeshGateAnnouncementProtocol, mask,
3282 NL80211_MESHCONF_GATE_ANNOUNCEMENTS,
3283 nla_get_u8);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003284 if (mask_out)
3285 *mask_out = mask;
Javier Cardonac80d5452010-12-16 17:37:49 -08003286
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003287 return 0;
3288
3289#undef FILL_IN_MESH_PARAM_IF_SET
3290}
3291
Javier Cardonac80d5452010-12-16 17:37:49 -08003292static int nl80211_parse_mesh_setup(struct genl_info *info,
3293 struct mesh_setup *setup)
3294{
3295 struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
3296
3297 if (!info->attrs[NL80211_ATTR_MESH_SETUP])
3298 return -EINVAL;
3299 if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
3300 info->attrs[NL80211_ATTR_MESH_SETUP],
3301 nl80211_mesh_setup_params_policy))
3302 return -EINVAL;
3303
3304 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
3305 setup->path_sel_proto =
3306 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
3307 IEEE80211_PATH_PROTOCOL_VENDOR :
3308 IEEE80211_PATH_PROTOCOL_HWMP;
3309
3310 if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
3311 setup->path_metric =
3312 (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
3313 IEEE80211_PATH_METRIC_VENDOR :
3314 IEEE80211_PATH_METRIC_AIRTIME;
3315
Javier Cardona581a8b02011-04-07 15:08:27 -07003316
3317 if (tb[NL80211_MESH_SETUP_IE]) {
Javier Cardonac80d5452010-12-16 17:37:49 -08003318 struct nlattr *ieattr =
Javier Cardona581a8b02011-04-07 15:08:27 -07003319 tb[NL80211_MESH_SETUP_IE];
Javier Cardonac80d5452010-12-16 17:37:49 -08003320 if (!is_valid_ie_attr(ieattr))
3321 return -EINVAL;
Javier Cardona581a8b02011-04-07 15:08:27 -07003322 setup->ie = nla_data(ieattr);
3323 setup->ie_len = nla_len(ieattr);
Javier Cardonac80d5452010-12-16 17:37:49 -08003324 }
Javier Cardonab130e5c2011-05-03 16:57:07 -07003325 setup->is_authenticated = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AUTH]);
3326 setup->is_secure = nla_get_flag(tb[NL80211_MESH_SETUP_USERSPACE_AMPE]);
Javier Cardonac80d5452010-12-16 17:37:49 -08003327
3328 return 0;
3329}
3330
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003331static int nl80211_update_mesh_config(struct sk_buff *skb,
Johannes Berg29cbe682010-12-03 09:20:44 +01003332 struct genl_info *info)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003333{
3334 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3335 struct net_device *dev = info->user_ptr[1];
Johannes Berg29cbe682010-12-03 09:20:44 +01003336 struct wireless_dev *wdev = dev->ieee80211_ptr;
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003337 struct mesh_config cfg;
3338 u32 mask;
3339 int err;
3340
Johannes Berg29cbe682010-12-03 09:20:44 +01003341 if (wdev->iftype != NL80211_IFTYPE_MESH_POINT)
3342 return -EOPNOTSUPP;
3343
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003344 if (!rdev->ops->update_mesh_config)
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003345 return -EOPNOTSUPP;
3346
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003347 err = nl80211_parse_mesh_config(info, &cfg, &mask);
Johannes Bergbd90fdc2010-12-03 09:20:43 +01003348 if (err)
3349 return err;
3350
Johannes Berg29cbe682010-12-03 09:20:44 +01003351 wdev_lock(wdev);
3352 if (!wdev->mesh_id_len)
3353 err = -ENOLINK;
3354
3355 if (!err)
Javier Cardona24bdd9f2010-12-16 17:37:48 -08003356 err = rdev->ops->update_mesh_config(&rdev->wiphy, dev,
Johannes Berg29cbe682010-12-03 09:20:44 +01003357 mask, &cfg);
3358
3359 wdev_unlock(wdev);
3360
3361 return err;
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07003362}
3363
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003364static int nl80211_get_reg(struct sk_buff *skb, struct genl_info *info)
3365{
3366 struct sk_buff *msg;
3367 void *hdr = NULL;
3368 struct nlattr *nl_reg_rules;
3369 unsigned int i;
3370 int err = -EINVAL;
3371
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003372 mutex_lock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003373
3374 if (!cfg80211_regdomain)
3375 goto out;
3376
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07003377 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003378 if (!msg) {
3379 err = -ENOBUFS;
3380 goto out;
3381 }
3382
3383 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
3384 NL80211_CMD_GET_REG);
3385 if (!hdr)
Julia Lawallefe1cf02011-01-28 15:17:11 +01003386 goto put_failure;
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003387
3388 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2,
3389 cfg80211_regdomain->alpha2);
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003390 if (cfg80211_regdomain->dfs_region)
3391 NLA_PUT_U8(msg, NL80211_ATTR_DFS_REGION,
3392 cfg80211_regdomain->dfs_region);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003393
3394 nl_reg_rules = nla_nest_start(msg, NL80211_ATTR_REG_RULES);
3395 if (!nl_reg_rules)
3396 goto nla_put_failure;
3397
3398 for (i = 0; i < cfg80211_regdomain->n_reg_rules; i++) {
3399 struct nlattr *nl_reg_rule;
3400 const struct ieee80211_reg_rule *reg_rule;
3401 const struct ieee80211_freq_range *freq_range;
3402 const struct ieee80211_power_rule *power_rule;
3403
3404 reg_rule = &cfg80211_regdomain->reg_rules[i];
3405 freq_range = &reg_rule->freq_range;
3406 power_rule = &reg_rule->power_rule;
3407
3408 nl_reg_rule = nla_nest_start(msg, i);
3409 if (!nl_reg_rule)
3410 goto nla_put_failure;
3411
3412 NLA_PUT_U32(msg, NL80211_ATTR_REG_RULE_FLAGS,
3413 reg_rule->flags);
3414 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_START,
3415 freq_range->start_freq_khz);
3416 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_END,
3417 freq_range->end_freq_khz);
3418 NLA_PUT_U32(msg, NL80211_ATTR_FREQ_RANGE_MAX_BW,
3419 freq_range->max_bandwidth_khz);
3420 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN,
3421 power_rule->max_antenna_gain);
3422 NLA_PUT_U32(msg, NL80211_ATTR_POWER_RULE_MAX_EIRP,
3423 power_rule->max_eirp);
3424
3425 nla_nest_end(msg, nl_reg_rule);
3426 }
3427
3428 nla_nest_end(msg, nl_reg_rules);
3429
3430 genlmsg_end(msg, hdr);
Johannes Berg134e6372009-07-10 09:51:34 +00003431 err = genlmsg_reply(msg, info);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003432 goto out;
3433
3434nla_put_failure:
3435 genlmsg_cancel(msg, hdr);
Julia Lawallefe1cf02011-01-28 15:17:11 +01003436put_failure:
Yuri Ershovd080e272010-06-29 15:08:07 +04003437 nlmsg_free(msg);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003438 err = -EMSGSIZE;
3439out:
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003440 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08003441 return err;
3442}
3443
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003444static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info)
3445{
3446 struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1];
3447 struct nlattr *nl_reg_rule;
3448 char *alpha2 = NULL;
3449 int rem_reg_rules = 0, r = 0;
3450 u32 num_rules = 0, rule_idx = 0, size_of_regd;
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003451 u8 dfs_region = 0;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003452 struct ieee80211_regdomain *rd = NULL;
3453
3454 if (!info->attrs[NL80211_ATTR_REG_ALPHA2])
3455 return -EINVAL;
3456
3457 if (!info->attrs[NL80211_ATTR_REG_RULES])
3458 return -EINVAL;
3459
3460 alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]);
3461
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003462 if (info->attrs[NL80211_ATTR_DFS_REGION])
3463 dfs_region = nla_get_u8(info->attrs[NL80211_ATTR_DFS_REGION]);
3464
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003465 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3466 rem_reg_rules) {
3467 num_rules++;
3468 if (num_rules > NL80211_MAX_SUPP_REG_RULES)
Luis R. Rodriguez4776c6e2009-05-13 17:04:39 -04003469 return -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003470 }
3471
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003472 mutex_lock(&cfg80211_mutex);
3473
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003474 if (!reg_is_valid_request(alpha2)) {
3475 r = -EINVAL;
3476 goto bad_reg;
3477 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003478
3479 size_of_regd = sizeof(struct ieee80211_regdomain) +
3480 (num_rules * sizeof(struct ieee80211_reg_rule));
3481
3482 rd = kzalloc(size_of_regd, GFP_KERNEL);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003483 if (!rd) {
3484 r = -ENOMEM;
3485 goto bad_reg;
3486 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003487
3488 rd->n_reg_rules = num_rules;
3489 rd->alpha2[0] = alpha2[0];
3490 rd->alpha2[1] = alpha2[1];
3491
Luis R. Rodriguez8b60b072011-10-11 10:59:02 -07003492 /*
3493 * Disable DFS master mode if the DFS region was
3494 * not supported or known on this kernel.
3495 */
3496 if (reg_supported_dfs_region(dfs_region))
3497 rd->dfs_region = dfs_region;
3498
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003499 nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES],
3500 rem_reg_rules) {
3501 nla_parse(tb, NL80211_REG_RULE_ATTR_MAX,
3502 nla_data(nl_reg_rule), nla_len(nl_reg_rule),
3503 reg_rule_policy);
3504 r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]);
3505 if (r)
3506 goto bad_reg;
3507
3508 rule_idx++;
3509
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003510 if (rule_idx > NL80211_MAX_SUPP_REG_RULES) {
3511 r = -EINVAL;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003512 goto bad_reg;
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003513 }
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003514 }
3515
3516 BUG_ON(rule_idx != num_rules);
3517
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003518 r = set_regdom(rd);
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003519
Luis R. Rodrigueza1794392009-02-21 00:04:21 -05003520 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003521
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003522 return r;
3523
Johannes Bergd2372b32008-10-24 20:32:20 +02003524 bad_reg:
Luis R. Rodriguez61405e92009-05-13 17:04:41 -04003525 mutex_unlock(&cfg80211_mutex);
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003526 kfree(rd);
Luis R. Rodriguezd0e18f82009-05-13 17:04:40 -04003527 return r;
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07003528}
3529
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003530static int validate_scan_freqs(struct nlattr *freqs)
3531{
3532 struct nlattr *attr1, *attr2;
3533 int n_channels = 0, tmp1, tmp2;
3534
3535 nla_for_each_nested(attr1, freqs, tmp1) {
3536 n_channels++;
3537 /*
3538 * Some hardware has a limited channel list for
3539 * scanning, and it is pretty much nonsensical
3540 * to scan for a channel twice, so disallow that
3541 * and don't require drivers to check that the
3542 * channel list they get isn't longer than what
3543 * they can scan, as long as they can scan all
3544 * the channels they registered at once.
3545 */
3546 nla_for_each_nested(attr2, freqs, tmp2)
3547 if (attr1 != attr2 &&
3548 nla_get_u32(attr1) == nla_get_u32(attr2))
3549 return 0;
3550 }
3551
3552 return n_channels;
3553}
3554
Johannes Berg2a519312009-02-10 21:25:55 +01003555static int nl80211_trigger_scan(struct sk_buff *skb, struct genl_info *info)
3556{
Johannes Berg4c476992010-10-04 21:36:35 +02003557 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3558 struct net_device *dev = info->user_ptr[1];
Johannes Berg2a519312009-02-10 21:25:55 +01003559 struct cfg80211_scan_request *request;
Johannes Berg2a519312009-02-10 21:25:55 +01003560 struct nlattr *attr;
3561 struct wiphy *wiphy;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003562 int err, tmp, n_ssids = 0, n_channels, i;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003563 size_t ie_len;
Johannes Berg2a519312009-02-10 21:25:55 +01003564
Johannes Bergf4a11bb2009-03-27 12:40:28 +01003565 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3566 return -EINVAL;
3567
Johannes Berg79c97e92009-07-07 03:56:12 +02003568 wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003569
Johannes Berg4c476992010-10-04 21:36:35 +02003570 if (!rdev->ops->scan)
3571 return -EOPNOTSUPP;
Johannes Berg2a519312009-02-10 21:25:55 +01003572
Johannes Berg4c476992010-10-04 21:36:35 +02003573 if (rdev->scan_req)
3574 return -EBUSY;
Johannes Berg2a519312009-02-10 21:25:55 +01003575
3576 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003577 n_channels = validate_scan_freqs(
3578 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
Johannes Berg4c476992010-10-04 21:36:35 +02003579 if (!n_channels)
3580 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003581 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003582 enum ieee80211_band band;
Johannes Berg83f5e2c2009-06-17 17:41:49 +02003583 n_channels = 0;
3584
Johannes Berg2a519312009-02-10 21:25:55 +01003585 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3586 if (wiphy->bands[band])
3587 n_channels += wiphy->bands[band]->n_channels;
3588 }
3589
3590 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3591 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp)
3592 n_ssids++;
3593
Johannes Berg4c476992010-10-04 21:36:35 +02003594 if (n_ssids > wiphy->max_scan_ssids)
3595 return -EINVAL;
Johannes Berg2a519312009-02-10 21:25:55 +01003596
Jouni Malinen70692ad2009-02-16 19:39:13 +02003597 if (info->attrs[NL80211_ATTR_IE])
3598 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3599 else
3600 ie_len = 0;
3601
Johannes Berg4c476992010-10-04 21:36:35 +02003602 if (ie_len > wiphy->max_scan_ie_len)
3603 return -EINVAL;
Johannes Berg18a83652009-03-31 12:12:05 +02003604
Johannes Berg2a519312009-02-10 21:25:55 +01003605 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003606 + sizeof(*request->ssids) * n_ssids
3607 + sizeof(*request->channels) * n_channels
Jouni Malinen70692ad2009-02-16 19:39:13 +02003608 + ie_len, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02003609 if (!request)
3610 return -ENOMEM;
Johannes Berg2a519312009-02-10 21:25:55 +01003611
Johannes Berg2a519312009-02-10 21:25:55 +01003612 if (n_ssids)
Johannes Berg5ba63532009-08-07 17:54:07 +02003613 request->ssids = (void *)&request->channels[n_channels];
Johannes Berg2a519312009-02-10 21:25:55 +01003614 request->n_ssids = n_ssids;
Jouni Malinen70692ad2009-02-16 19:39:13 +02003615 if (ie_len) {
3616 if (request->ssids)
3617 request->ie = (void *)(request->ssids + n_ssids);
3618 else
3619 request->ie = (void *)(request->channels + n_channels);
3620 }
Johannes Berg2a519312009-02-10 21:25:55 +01003621
Johannes Berg584991d2009-11-02 13:32:03 +01003622 i = 0;
Johannes Berg2a519312009-02-10 21:25:55 +01003623 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3624 /* user specified, bail out if channel not found */
Johannes Berg2a519312009-02-10 21:25:55 +01003625 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_FREQUENCIES], tmp) {
Johannes Berg584991d2009-11-02 13:32:03 +01003626 struct ieee80211_channel *chan;
3627
3628 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3629
3630 if (!chan) {
Johannes Berg2a519312009-02-10 21:25:55 +01003631 err = -EINVAL;
3632 goto out_free;
3633 }
Johannes Berg584991d2009-11-02 13:32:03 +01003634
3635 /* ignore disabled channels */
3636 if (chan->flags & IEEE80211_CHAN_DISABLED)
3637 continue;
3638
3639 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003640 i++;
3641 }
3642 } else {
Johannes Berg34850ab2011-07-18 18:08:35 +02003643 enum ieee80211_band band;
3644
Johannes Berg2a519312009-02-10 21:25:55 +01003645 /* all channels */
Johannes Berg2a519312009-02-10 21:25:55 +01003646 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3647 int j;
3648 if (!wiphy->bands[band])
3649 continue;
3650 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
Johannes Berg584991d2009-11-02 13:32:03 +01003651 struct ieee80211_channel *chan;
3652
3653 chan = &wiphy->bands[band]->channels[j];
3654
3655 if (chan->flags & IEEE80211_CHAN_DISABLED)
3656 continue;
3657
3658 request->channels[i] = chan;
Johannes Berg2a519312009-02-10 21:25:55 +01003659 i++;
3660 }
3661 }
3662 }
3663
Johannes Berg584991d2009-11-02 13:32:03 +01003664 if (!i) {
3665 err = -EINVAL;
3666 goto out_free;
3667 }
3668
3669 request->n_channels = i;
3670
Johannes Berg2a519312009-02-10 21:25:55 +01003671 i = 0;
3672 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3673 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS], tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003674 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Johannes Berg2a519312009-02-10 21:25:55 +01003675 err = -EINVAL;
3676 goto out_free;
3677 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003678 request->ssids[i].ssid_len = nla_len(attr);
Johannes Berg2a519312009-02-10 21:25:55 +01003679 memcpy(request->ssids[i].ssid, nla_data(attr), nla_len(attr));
Johannes Berg2a519312009-02-10 21:25:55 +01003680 i++;
3681 }
3682 }
3683
Jouni Malinen70692ad2009-02-16 19:39:13 +02003684 if (info->attrs[NL80211_ATTR_IE]) {
3685 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Johannes Bergde95a542009-04-01 11:58:36 +02003686 memcpy((void *)request->ie,
3687 nla_data(info->attrs[NL80211_ATTR_IE]),
Jouni Malinen70692ad2009-02-16 19:39:13 +02003688 request->ie_len);
3689 }
3690
Johannes Berg34850ab2011-07-18 18:08:35 +02003691 for (i = 0; i < IEEE80211_NUM_BANDS; i++)
Johannes Berga401d2b2011-07-20 00:52:16 +02003692 if (wiphy->bands[i])
3693 request->rates[i] =
3694 (1 << wiphy->bands[i]->n_bitrates) - 1;
Johannes Berg34850ab2011-07-18 18:08:35 +02003695
3696 if (info->attrs[NL80211_ATTR_SCAN_SUPP_RATES]) {
3697 nla_for_each_nested(attr,
3698 info->attrs[NL80211_ATTR_SCAN_SUPP_RATES],
3699 tmp) {
3700 enum ieee80211_band band = nla_type(attr);
3701
Dan Carpenter84404622011-07-29 11:52:18 +03003702 if (band < 0 || band >= IEEE80211_NUM_BANDS) {
Johannes Berg34850ab2011-07-18 18:08:35 +02003703 err = -EINVAL;
3704 goto out_free;
3705 }
3706 err = ieee80211_get_ratemask(wiphy->bands[band],
3707 nla_data(attr),
3708 nla_len(attr),
3709 &request->rates[band]);
3710 if (err)
3711 goto out_free;
3712 }
3713 }
3714
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05303715 request->no_cck =
3716 nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
3717
Johannes Berg463d0182009-07-14 00:33:35 +02003718 request->dev = dev;
Johannes Berg79c97e92009-07-07 03:56:12 +02003719 request->wiphy = &rdev->wiphy;
Johannes Berg2a519312009-02-10 21:25:55 +01003720
Johannes Berg79c97e92009-07-07 03:56:12 +02003721 rdev->scan_req = request;
3722 err = rdev->ops->scan(&rdev->wiphy, dev, request);
Johannes Berg2a519312009-02-10 21:25:55 +01003723
Johannes Berg463d0182009-07-14 00:33:35 +02003724 if (!err) {
Johannes Berg79c97e92009-07-07 03:56:12 +02003725 nl80211_send_scan_start(rdev, dev);
Johannes Berg463d0182009-07-14 00:33:35 +02003726 dev_hold(dev);
Johannes Berg4c476992010-10-04 21:36:35 +02003727 } else {
Johannes Berg2a519312009-02-10 21:25:55 +01003728 out_free:
Johannes Berg79c97e92009-07-07 03:56:12 +02003729 rdev->scan_req = NULL;
Johannes Berg2a519312009-02-10 21:25:55 +01003730 kfree(request);
3731 }
Johannes Berg3b858752009-03-12 09:55:09 +01003732
Johannes Berg2a519312009-02-10 21:25:55 +01003733 return err;
3734}
3735
Luciano Coelho807f8a82011-05-11 17:09:35 +03003736static int nl80211_start_sched_scan(struct sk_buff *skb,
3737 struct genl_info *info)
3738{
3739 struct cfg80211_sched_scan_request *request;
3740 struct cfg80211_registered_device *rdev = info->user_ptr[0];
3741 struct net_device *dev = info->user_ptr[1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003742 struct nlattr *attr;
3743 struct wiphy *wiphy;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003744 int err, tmp, n_ssids = 0, n_match_sets = 0, n_channels, i;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003745 u32 interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003746 enum ieee80211_band band;
3747 size_t ie_len;
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003748 struct nlattr *tb[NL80211_SCHED_SCAN_MATCH_ATTR_MAX + 1];
Luciano Coelho807f8a82011-05-11 17:09:35 +03003749
3750 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3751 !rdev->ops->sched_scan_start)
3752 return -EOPNOTSUPP;
3753
3754 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
3755 return -EINVAL;
3756
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003757 if (!info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL])
3758 return -EINVAL;
3759
3760 interval = nla_get_u32(info->attrs[NL80211_ATTR_SCHED_SCAN_INTERVAL]);
3761 if (interval == 0)
3762 return -EINVAL;
3763
Luciano Coelho807f8a82011-05-11 17:09:35 +03003764 wiphy = &rdev->wiphy;
3765
3766 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3767 n_channels = validate_scan_freqs(
3768 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]);
3769 if (!n_channels)
3770 return -EINVAL;
3771 } else {
3772 n_channels = 0;
3773
3774 for (band = 0; band < IEEE80211_NUM_BANDS; band++)
3775 if (wiphy->bands[band])
3776 n_channels += wiphy->bands[band]->n_channels;
3777 }
3778
3779 if (info->attrs[NL80211_ATTR_SCAN_SSIDS])
3780 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3781 tmp)
3782 n_ssids++;
3783
Luciano Coelho93b6aa62011-07-13 14:57:28 +03003784 if (n_ssids > wiphy->max_sched_scan_ssids)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003785 return -EINVAL;
3786
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003787 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH])
3788 nla_for_each_nested(attr,
3789 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
3790 tmp)
3791 n_match_sets++;
3792
3793 if (n_match_sets > wiphy->max_match_sets)
3794 return -EINVAL;
3795
Luciano Coelho807f8a82011-05-11 17:09:35 +03003796 if (info->attrs[NL80211_ATTR_IE])
3797 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3798 else
3799 ie_len = 0;
3800
Luciano Coelho5a865ba2011-07-13 14:57:29 +03003801 if (ie_len > wiphy->max_sched_scan_ie_len)
Luciano Coelho807f8a82011-05-11 17:09:35 +03003802 return -EINVAL;
3803
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003804 mutex_lock(&rdev->sched_scan_mtx);
3805
3806 if (rdev->sched_scan_req) {
3807 err = -EINPROGRESS;
3808 goto out;
3809 }
3810
Luciano Coelho807f8a82011-05-11 17:09:35 +03003811 request = kzalloc(sizeof(*request)
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003812 + sizeof(*request->ssids) * n_ssids
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003813 + sizeof(*request->match_sets) * n_match_sets
Luciano Coelhoa2cd43c2011-05-18 11:42:03 +03003814 + sizeof(*request->channels) * n_channels
Luciano Coelho807f8a82011-05-11 17:09:35 +03003815 + ie_len, GFP_KERNEL);
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003816 if (!request) {
3817 err = -ENOMEM;
3818 goto out;
3819 }
Luciano Coelho807f8a82011-05-11 17:09:35 +03003820
3821 if (n_ssids)
3822 request->ssids = (void *)&request->channels[n_channels];
3823 request->n_ssids = n_ssids;
3824 if (ie_len) {
3825 if (request->ssids)
3826 request->ie = (void *)(request->ssids + n_ssids);
3827 else
3828 request->ie = (void *)(request->channels + n_channels);
3829 }
3830
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003831 if (n_match_sets) {
3832 if (request->ie)
3833 request->match_sets = (void *)(request->ie + ie_len);
3834 else if (request->ssids)
3835 request->match_sets =
3836 (void *)(request->ssids + n_ssids);
3837 else
3838 request->match_sets =
3839 (void *)(request->channels + n_channels);
3840 }
3841 request->n_match_sets = n_match_sets;
3842
Luciano Coelho807f8a82011-05-11 17:09:35 +03003843 i = 0;
3844 if (info->attrs[NL80211_ATTR_SCAN_FREQUENCIES]) {
3845 /* user specified, bail out if channel not found */
3846 nla_for_each_nested(attr,
3847 info->attrs[NL80211_ATTR_SCAN_FREQUENCIES],
3848 tmp) {
3849 struct ieee80211_channel *chan;
3850
3851 chan = ieee80211_get_channel(wiphy, nla_get_u32(attr));
3852
3853 if (!chan) {
3854 err = -EINVAL;
3855 goto out_free;
3856 }
3857
3858 /* ignore disabled channels */
3859 if (chan->flags & IEEE80211_CHAN_DISABLED)
3860 continue;
3861
3862 request->channels[i] = chan;
3863 i++;
3864 }
3865 } else {
3866 /* all channels */
3867 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
3868 int j;
3869 if (!wiphy->bands[band])
3870 continue;
3871 for (j = 0; j < wiphy->bands[band]->n_channels; j++) {
3872 struct ieee80211_channel *chan;
3873
3874 chan = &wiphy->bands[band]->channels[j];
3875
3876 if (chan->flags & IEEE80211_CHAN_DISABLED)
3877 continue;
3878
3879 request->channels[i] = chan;
3880 i++;
3881 }
3882 }
3883 }
3884
3885 if (!i) {
3886 err = -EINVAL;
3887 goto out_free;
3888 }
3889
3890 request->n_channels = i;
3891
3892 i = 0;
3893 if (info->attrs[NL80211_ATTR_SCAN_SSIDS]) {
3894 nla_for_each_nested(attr, info->attrs[NL80211_ATTR_SCAN_SSIDS],
3895 tmp) {
Luciano Coelho57a27e12011-06-07 20:42:26 +03003896 if (nla_len(attr) > IEEE80211_MAX_SSID_LEN) {
Luciano Coelho807f8a82011-05-11 17:09:35 +03003897 err = -EINVAL;
3898 goto out_free;
3899 }
Luciano Coelho57a27e12011-06-07 20:42:26 +03003900 request->ssids[i].ssid_len = nla_len(attr);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003901 memcpy(request->ssids[i].ssid, nla_data(attr),
3902 nla_len(attr));
Luciano Coelho807f8a82011-05-11 17:09:35 +03003903 i++;
3904 }
3905 }
3906
Luciano Coelhoa1f1c212011-08-31 16:01:48 +03003907 i = 0;
3908 if (info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH]) {
3909 nla_for_each_nested(attr,
3910 info->attrs[NL80211_ATTR_SCHED_SCAN_MATCH],
3911 tmp) {
3912 struct nlattr *ssid;
3913
3914 nla_parse(tb, NL80211_SCHED_SCAN_MATCH_ATTR_MAX,
3915 nla_data(attr), nla_len(attr),
3916 nl80211_match_policy);
3917 ssid = tb[NL80211_ATTR_SCHED_SCAN_MATCH_SSID];
3918 if (ssid) {
3919 if (nla_len(ssid) > IEEE80211_MAX_SSID_LEN) {
3920 err = -EINVAL;
3921 goto out_free;
3922 }
3923 memcpy(request->match_sets[i].ssid.ssid,
3924 nla_data(ssid), nla_len(ssid));
3925 request->match_sets[i].ssid.ssid_len =
3926 nla_len(ssid);
3927 }
3928 i++;
3929 }
3930 }
3931
Luciano Coelho807f8a82011-05-11 17:09:35 +03003932 if (info->attrs[NL80211_ATTR_IE]) {
3933 request->ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
3934 memcpy((void *)request->ie,
3935 nla_data(info->attrs[NL80211_ATTR_IE]),
3936 request->ie_len);
3937 }
3938
3939 request->dev = dev;
3940 request->wiphy = &rdev->wiphy;
Luciano Coelhobbe6ad62011-05-11 17:09:37 +03003941 request->interval = interval;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003942
3943 err = rdev->ops->sched_scan_start(&rdev->wiphy, dev, request);
3944 if (!err) {
3945 rdev->sched_scan_req = request;
3946 nl80211_send_sched_scan(rdev, dev,
3947 NL80211_CMD_START_SCHED_SCAN);
3948 goto out;
3949 }
3950
3951out_free:
3952 kfree(request);
3953out:
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003954 mutex_unlock(&rdev->sched_scan_mtx);
Luciano Coelho807f8a82011-05-11 17:09:35 +03003955 return err;
3956}
3957
3958static int nl80211_stop_sched_scan(struct sk_buff *skb,
3959 struct genl_info *info)
3960{
3961 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003962 int err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003963
3964 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_SCHED_SCAN) ||
3965 !rdev->ops->sched_scan_stop)
3966 return -EOPNOTSUPP;
3967
Luciano Coelhoc10841c2011-06-30 08:32:41 +03003968 mutex_lock(&rdev->sched_scan_mtx);
3969 err = __cfg80211_stop_sched_scan(rdev, false);
3970 mutex_unlock(&rdev->sched_scan_mtx);
3971
3972 return err;
Luciano Coelho807f8a82011-05-11 17:09:35 +03003973}
3974
Johannes Berg9720bb32011-06-21 09:45:33 +02003975static int nl80211_send_bss(struct sk_buff *msg, struct netlink_callback *cb,
3976 u32 seq, int flags,
Johannes Berg2a519312009-02-10 21:25:55 +01003977 struct cfg80211_registered_device *rdev,
Johannes Berg48ab9052009-07-10 18:42:31 +02003978 struct wireless_dev *wdev,
3979 struct cfg80211_internal_bss *intbss)
Johannes Berg2a519312009-02-10 21:25:55 +01003980{
Johannes Berg48ab9052009-07-10 18:42:31 +02003981 struct cfg80211_bss *res = &intbss->pub;
Johannes Berg2a519312009-02-10 21:25:55 +01003982 void *hdr;
3983 struct nlattr *bss;
Johannes Berg48ab9052009-07-10 18:42:31 +02003984 int i;
3985
3986 ASSERT_WDEV_LOCK(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01003987
Johannes Berg9720bb32011-06-21 09:45:33 +02003988 hdr = nl80211hdr_put(msg, NETLINK_CB(cb->skb).pid, seq, flags,
Johannes Berg2a519312009-02-10 21:25:55 +01003989 NL80211_CMD_NEW_SCAN_RESULTS);
3990 if (!hdr)
3991 return -1;
3992
Johannes Berg9720bb32011-06-21 09:45:33 +02003993 genl_dump_check_consistent(cb, hdr, &nl80211_fam);
3994
Johannes Bergf5ea9122009-08-07 16:17:38 +02003995 NLA_PUT_U32(msg, NL80211_ATTR_GENERATION, rdev->bss_generation);
Johannes Berg48ab9052009-07-10 18:42:31 +02003996 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, wdev->netdev->ifindex);
Johannes Berg2a519312009-02-10 21:25:55 +01003997
3998 bss = nla_nest_start(msg, NL80211_ATTR_BSS);
3999 if (!bss)
4000 goto nla_put_failure;
4001 if (!is_zero_ether_addr(res->bssid))
4002 NLA_PUT(msg, NL80211_BSS_BSSID, ETH_ALEN, res->bssid);
4003 if (res->information_elements && res->len_information_elements)
4004 NLA_PUT(msg, NL80211_BSS_INFORMATION_ELEMENTS,
4005 res->len_information_elements,
4006 res->information_elements);
Jouni Malinen34a6edd2010-01-06 16:19:24 +02004007 if (res->beacon_ies && res->len_beacon_ies &&
4008 res->beacon_ies != res->information_elements)
4009 NLA_PUT(msg, NL80211_BSS_BEACON_IES,
4010 res->len_beacon_ies, res->beacon_ies);
Johannes Berg2a519312009-02-10 21:25:55 +01004011 if (res->tsf)
4012 NLA_PUT_U64(msg, NL80211_BSS_TSF, res->tsf);
4013 if (res->beacon_interval)
4014 NLA_PUT_U16(msg, NL80211_BSS_BEACON_INTERVAL, res->beacon_interval);
4015 NLA_PUT_U16(msg, NL80211_BSS_CAPABILITY, res->capability);
4016 NLA_PUT_U32(msg, NL80211_BSS_FREQUENCY, res->channel->center_freq);
Holger Schurig7c896062009-09-24 12:21:01 +02004017 NLA_PUT_U32(msg, NL80211_BSS_SEEN_MS_AGO,
4018 jiffies_to_msecs(jiffies - intbss->ts));
Johannes Berg2a519312009-02-10 21:25:55 +01004019
Johannes Berg77965c92009-02-18 18:45:06 +01004020 switch (rdev->wiphy.signal_type) {
Johannes Berg2a519312009-02-10 21:25:55 +01004021 case CFG80211_SIGNAL_TYPE_MBM:
4022 NLA_PUT_U32(msg, NL80211_BSS_SIGNAL_MBM, res->signal);
4023 break;
4024 case CFG80211_SIGNAL_TYPE_UNSPEC:
4025 NLA_PUT_U8(msg, NL80211_BSS_SIGNAL_UNSPEC, res->signal);
4026 break;
4027 default:
4028 break;
4029 }
4030
Johannes Berg48ab9052009-07-10 18:42:31 +02004031 switch (wdev->iftype) {
Johannes Berg074ac8d2010-09-16 14:58:22 +02004032 case NL80211_IFTYPE_P2P_CLIENT:
Johannes Berg48ab9052009-07-10 18:42:31 +02004033 case NL80211_IFTYPE_STATION:
4034 if (intbss == wdev->current_bss)
4035 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4036 NL80211_BSS_STATUS_ASSOCIATED);
4037 else for (i = 0; i < MAX_AUTH_BSSES; i++) {
4038 if (intbss != wdev->auth_bsses[i])
4039 continue;
4040 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4041 NL80211_BSS_STATUS_AUTHENTICATED);
4042 break;
4043 }
4044 break;
4045 case NL80211_IFTYPE_ADHOC:
4046 if (intbss == wdev->current_bss)
4047 NLA_PUT_U32(msg, NL80211_BSS_STATUS,
4048 NL80211_BSS_STATUS_IBSS_JOINED);
4049 break;
4050 default:
4051 break;
4052 }
4053
Johannes Berg2a519312009-02-10 21:25:55 +01004054 nla_nest_end(msg, bss);
4055
4056 return genlmsg_end(msg, hdr);
4057
4058 nla_put_failure:
4059 genlmsg_cancel(msg, hdr);
4060 return -EMSGSIZE;
4061}
4062
4063static int nl80211_dump_scan(struct sk_buff *skb,
4064 struct netlink_callback *cb)
4065{
Johannes Berg48ab9052009-07-10 18:42:31 +02004066 struct cfg80211_registered_device *rdev;
4067 struct net_device *dev;
Johannes Berg2a519312009-02-10 21:25:55 +01004068 struct cfg80211_internal_bss *scan;
Johannes Berg48ab9052009-07-10 18:42:31 +02004069 struct wireless_dev *wdev;
Johannes Berg2a519312009-02-10 21:25:55 +01004070 int start = cb->args[1], idx = 0;
4071 int err;
4072
Johannes Berg67748892010-10-04 21:14:06 +02004073 err = nl80211_prepare_netdev_dump(skb, cb, &rdev, &dev);
4074 if (err)
4075 return err;
Johannes Berg2a519312009-02-10 21:25:55 +01004076
Johannes Berg48ab9052009-07-10 18:42:31 +02004077 wdev = dev->ieee80211_ptr;
Johannes Berg2a519312009-02-10 21:25:55 +01004078
Johannes Berg48ab9052009-07-10 18:42:31 +02004079 wdev_lock(wdev);
4080 spin_lock_bh(&rdev->bss_lock);
4081 cfg80211_bss_expire(rdev);
4082
Johannes Berg9720bb32011-06-21 09:45:33 +02004083 cb->seq = rdev->bss_generation;
4084
Johannes Berg48ab9052009-07-10 18:42:31 +02004085 list_for_each_entry(scan, &rdev->bss_list, list) {
Johannes Berg2a519312009-02-10 21:25:55 +01004086 if (++idx <= start)
4087 continue;
Johannes Berg9720bb32011-06-21 09:45:33 +02004088 if (nl80211_send_bss(skb, cb,
Johannes Berg2a519312009-02-10 21:25:55 +01004089 cb->nlh->nlmsg_seq, NLM_F_MULTI,
Johannes Berg48ab9052009-07-10 18:42:31 +02004090 rdev, wdev, scan) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01004091 idx--;
Johannes Berg67748892010-10-04 21:14:06 +02004092 break;
Johannes Berg2a519312009-02-10 21:25:55 +01004093 }
4094 }
4095
Johannes Berg48ab9052009-07-10 18:42:31 +02004096 spin_unlock_bh(&rdev->bss_lock);
4097 wdev_unlock(wdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004098
4099 cb->args[1] = idx;
Johannes Berg67748892010-10-04 21:14:06 +02004100 nl80211_finish_netdev_dump(rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01004101
Johannes Berg67748892010-10-04 21:14:06 +02004102 return skb->len;
Johannes Berg2a519312009-02-10 21:25:55 +01004103}
4104
Holger Schurig61fa7132009-11-11 12:25:40 +01004105static int nl80211_send_survey(struct sk_buff *msg, u32 pid, u32 seq,
4106 int flags, struct net_device *dev,
4107 struct survey_info *survey)
4108{
4109 void *hdr;
4110 struct nlattr *infoattr;
4111
Holger Schurig61fa7132009-11-11 12:25:40 +01004112 hdr = nl80211hdr_put(msg, pid, seq, flags,
4113 NL80211_CMD_NEW_SURVEY_RESULTS);
4114 if (!hdr)
4115 return -ENOMEM;
4116
4117 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
4118
4119 infoattr = nla_nest_start(msg, NL80211_ATTR_SURVEY_INFO);
4120 if (!infoattr)
4121 goto nla_put_failure;
4122
4123 NLA_PUT_U32(msg, NL80211_SURVEY_INFO_FREQUENCY,
4124 survey->channel->center_freq);
4125 if (survey->filled & SURVEY_INFO_NOISE_DBM)
4126 NLA_PUT_U8(msg, NL80211_SURVEY_INFO_NOISE,
4127 survey->noise);
Felix Fietkau17e5a802010-09-29 17:15:30 +02004128 if (survey->filled & SURVEY_INFO_IN_USE)
4129 NLA_PUT_FLAG(msg, NL80211_SURVEY_INFO_IN_USE);
Felix Fietkau8610c292010-10-09 02:39:29 +02004130 if (survey->filled & SURVEY_INFO_CHANNEL_TIME)
4131 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME,
4132 survey->channel_time);
4133 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_BUSY)
4134 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_BUSY,
4135 survey->channel_time_busy);
4136 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_EXT_BUSY)
4137 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_EXT_BUSY,
4138 survey->channel_time_ext_busy);
4139 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_RX)
4140 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_RX,
4141 survey->channel_time_rx);
4142 if (survey->filled & SURVEY_INFO_CHANNEL_TIME_TX)
4143 NLA_PUT_U64(msg, NL80211_SURVEY_INFO_CHANNEL_TIME_TX,
4144 survey->channel_time_tx);
Holger Schurig61fa7132009-11-11 12:25:40 +01004145
4146 nla_nest_end(msg, infoattr);
4147
4148 return genlmsg_end(msg, hdr);
4149
4150 nla_put_failure:
4151 genlmsg_cancel(msg, hdr);
4152 return -EMSGSIZE;
4153}
4154
4155static int nl80211_dump_survey(struct sk_buff *skb,
4156 struct netlink_callback *cb)
4157{
4158 struct survey_info survey;
4159 struct cfg80211_registered_device *dev;
4160 struct net_device *netdev;
Holger Schurig61fa7132009-11-11 12:25:40 +01004161 int survey_idx = cb->args[1];
4162 int res;
4163
Johannes Berg67748892010-10-04 21:14:06 +02004164 res = nl80211_prepare_netdev_dump(skb, cb, &dev, &netdev);
4165 if (res)
4166 return res;
Holger Schurig61fa7132009-11-11 12:25:40 +01004167
4168 if (!dev->ops->dump_survey) {
4169 res = -EOPNOTSUPP;
4170 goto out_err;
4171 }
4172
4173 while (1) {
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004174 struct ieee80211_channel *chan;
4175
Holger Schurig61fa7132009-11-11 12:25:40 +01004176 res = dev->ops->dump_survey(&dev->wiphy, netdev, survey_idx,
4177 &survey);
4178 if (res == -ENOENT)
4179 break;
4180 if (res)
4181 goto out_err;
4182
Luis R. Rodriguez180cdc72011-05-27 07:24:02 -07004183 /* Survey without a channel doesn't make sense */
4184 if (!survey.channel) {
4185 res = -EINVAL;
4186 goto out;
4187 }
4188
4189 chan = ieee80211_get_channel(&dev->wiphy,
4190 survey.channel->center_freq);
4191 if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) {
4192 survey_idx++;
4193 continue;
4194 }
4195
Holger Schurig61fa7132009-11-11 12:25:40 +01004196 if (nl80211_send_survey(skb,
4197 NETLINK_CB(cb->skb).pid,
4198 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4199 netdev,
4200 &survey) < 0)
4201 goto out;
4202 survey_idx++;
4203 }
4204
4205 out:
4206 cb->args[1] = survey_idx;
4207 res = skb->len;
4208 out_err:
Johannes Berg67748892010-10-04 21:14:06 +02004209 nl80211_finish_netdev_dump(dev);
Holger Schurig61fa7132009-11-11 12:25:40 +01004210 return res;
4211}
4212
Jouni Malinen255e7372009-03-20 21:21:17 +02004213static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type)
4214{
Samuel Ortizb23aa672009-07-01 21:26:54 +02004215 return auth_type <= NL80211_AUTHTYPE_MAX;
Jouni Malinen255e7372009-03-20 21:21:17 +02004216}
4217
Samuel Ortizb23aa672009-07-01 21:26:54 +02004218static bool nl80211_valid_wpa_versions(u32 wpa_versions)
4219{
4220 return !(wpa_versions & ~(NL80211_WPA_VERSION_1 |
4221 NL80211_WPA_VERSION_2));
4222}
4223
Jouni Malinen636a5d32009-03-19 13:39:22 +02004224static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info)
4225{
Johannes Berg4c476992010-10-04 21:36:35 +02004226 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4227 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004228 struct ieee80211_channel *chan;
4229 const u8 *bssid, *ssid, *ie = NULL;
4230 int err, ssid_len, ie_len = 0;
4231 enum nl80211_auth_type auth_type;
Johannes Bergfffd0932009-07-08 14:22:54 +02004232 struct key_parse key;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004233 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004234
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004235 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4236 return -EINVAL;
4237
4238 if (!info->attrs[NL80211_ATTR_MAC])
4239 return -EINVAL;
4240
Jouni Malinen17780922009-03-27 20:52:47 +02004241 if (!info->attrs[NL80211_ATTR_AUTH_TYPE])
4242 return -EINVAL;
4243
Johannes Berg19957bb2009-07-02 17:20:43 +02004244 if (!info->attrs[NL80211_ATTR_SSID])
4245 return -EINVAL;
4246
4247 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ])
4248 return -EINVAL;
4249
Johannes Bergfffd0932009-07-08 14:22:54 +02004250 err = nl80211_parse_key(info, &key);
4251 if (err)
4252 return err;
4253
4254 if (key.idx >= 0) {
Johannes Berge31b8212010-10-05 19:39:30 +02004255 if (key.type != -1 && key.type != NL80211_KEYTYPE_GROUP)
4256 return -EINVAL;
Johannes Bergfffd0932009-07-08 14:22:54 +02004257 if (!key.p.key || !key.p.key_len)
4258 return -EINVAL;
4259 if ((key.p.cipher != WLAN_CIPHER_SUITE_WEP40 ||
4260 key.p.key_len != WLAN_KEY_LEN_WEP40) &&
4261 (key.p.cipher != WLAN_CIPHER_SUITE_WEP104 ||
4262 key.p.key_len != WLAN_KEY_LEN_WEP104))
4263 return -EINVAL;
4264 if (key.idx > 4)
4265 return -EINVAL;
4266 } else {
4267 key.p.key_len = 0;
4268 key.p.key = NULL;
4269 }
4270
Johannes Bergafea0b72010-08-10 09:46:42 +02004271 if (key.idx >= 0) {
4272 int i;
4273 bool ok = false;
4274 for (i = 0; i < rdev->wiphy.n_cipher_suites; i++) {
4275 if (key.p.cipher == rdev->wiphy.cipher_suites[i]) {
4276 ok = true;
4277 break;
4278 }
4279 }
Johannes Berg4c476992010-10-04 21:36:35 +02004280 if (!ok)
4281 return -EINVAL;
Johannes Bergafea0b72010-08-10 09:46:42 +02004282 }
4283
Johannes Berg4c476992010-10-04 21:36:35 +02004284 if (!rdev->ops->auth)
4285 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004286
Johannes Berg074ac8d2010-09-16 14:58:22 +02004287 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004288 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4289 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004290
Johannes Berg19957bb2009-07-02 17:20:43 +02004291 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg79c97e92009-07-07 03:56:12 +02004292 chan = ieee80211_get_channel(&rdev->wiphy,
Johannes Berg19957bb2009-07-02 17:20:43 +02004293 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004294 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4295 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004296
Johannes Berg19957bb2009-07-02 17:20:43 +02004297 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4298 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4299
4300 if (info->attrs[NL80211_ATTR_IE]) {
4301 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4302 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4303 }
4304
4305 auth_type = nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
Johannes Berg4c476992010-10-04 21:36:35 +02004306 if (!nl80211_valid_auth_type(auth_type))
4307 return -EINVAL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004308
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004309 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4310
Johannes Berg4c476992010-10-04 21:36:35 +02004311 return cfg80211_mlme_auth(rdev, dev, chan, auth_type, bssid,
4312 ssid, ssid_len, ie, ie_len,
4313 key.p.key, key.p.key_len, key.idx,
4314 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004315}
4316
Johannes Bergc0692b82010-08-27 14:26:53 +03004317static int nl80211_crypto_settings(struct cfg80211_registered_device *rdev,
4318 struct genl_info *info,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004319 struct cfg80211_crypto_settings *settings,
4320 int cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004321{
Johannes Bergc0b2bbd2009-07-25 16:54:36 +02004322 memset(settings, 0, sizeof(*settings));
4323
Samuel Ortizb23aa672009-07-01 21:26:54 +02004324 settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT];
4325
Johannes Bergc0692b82010-08-27 14:26:53 +03004326 if (info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]) {
4327 u16 proto;
4328 proto = nla_get_u16(
4329 info->attrs[NL80211_ATTR_CONTROL_PORT_ETHERTYPE]);
4330 settings->control_port_ethertype = cpu_to_be16(proto);
4331 if (!(rdev->wiphy.flags & WIPHY_FLAG_CONTROL_PORT_PROTOCOL) &&
4332 proto != ETH_P_PAE)
4333 return -EINVAL;
4334 if (info->attrs[NL80211_ATTR_CONTROL_PORT_NO_ENCRYPT])
4335 settings->control_port_no_encrypt = true;
4336 } else
4337 settings->control_port_ethertype = cpu_to_be16(ETH_P_PAE);
4338
Samuel Ortizb23aa672009-07-01 21:26:54 +02004339 if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) {
4340 void *data;
4341 int len, i;
4342
4343 data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4344 len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]);
4345 settings->n_ciphers_pairwise = len / sizeof(u32);
4346
4347 if (len % sizeof(u32))
4348 return -EINVAL;
4349
Johannes Berg3dc27d22009-07-02 21:36:37 +02004350 if (settings->n_ciphers_pairwise > cipher_limit)
Samuel Ortizb23aa672009-07-01 21:26:54 +02004351 return -EINVAL;
4352
4353 memcpy(settings->ciphers_pairwise, data, len);
4354
4355 for (i = 0; i < settings->n_ciphers_pairwise; i++)
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004356 if (!cfg80211_supported_cipher_suite(
4357 &rdev->wiphy,
Samuel Ortizb23aa672009-07-01 21:26:54 +02004358 settings->ciphers_pairwise[i]))
4359 return -EINVAL;
4360 }
4361
4362 if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) {
4363 settings->cipher_group =
4364 nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]);
Jouni Malinen38ba3c52011-09-21 18:14:56 +03004365 if (!cfg80211_supported_cipher_suite(&rdev->wiphy,
4366 settings->cipher_group))
Samuel Ortizb23aa672009-07-01 21:26:54 +02004367 return -EINVAL;
4368 }
4369
4370 if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) {
4371 settings->wpa_versions =
4372 nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]);
4373 if (!nl80211_valid_wpa_versions(settings->wpa_versions))
4374 return -EINVAL;
4375 }
4376
4377 if (info->attrs[NL80211_ATTR_AKM_SUITES]) {
4378 void *data;
Jouni Malinen6d302402011-09-21 18:11:33 +03004379 int len;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004380
4381 data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]);
4382 len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]);
4383 settings->n_akm_suites = len / sizeof(u32);
4384
4385 if (len % sizeof(u32))
4386 return -EINVAL;
4387
Jouni Malinen1b9ca022011-09-21 16:13:07 +03004388 if (settings->n_akm_suites > NL80211_MAX_NR_AKM_SUITES)
4389 return -EINVAL;
4390
Samuel Ortizb23aa672009-07-01 21:26:54 +02004391 memcpy(settings->akm_suites, data, len);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004392 }
4393
4394 return 0;
4395}
4396
Jouni Malinen636a5d32009-03-19 13:39:22 +02004397static int nl80211_associate(struct sk_buff *skb, struct genl_info *info)
4398{
Johannes Berg4c476992010-10-04 21:36:35 +02004399 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4400 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004401 struct cfg80211_crypto_settings crypto;
Johannes Bergf444de02010-05-05 15:25:02 +02004402 struct ieee80211_channel *chan;
Johannes Berg3e5d7642009-07-07 14:37:26 +02004403 const u8 *bssid, *ssid, *ie = NULL, *prev_bssid = NULL;
Johannes Berg19957bb2009-07-02 17:20:43 +02004404 int err, ssid_len, ie_len = 0;
4405 bool use_mfp = false;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004406
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004407 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4408 return -EINVAL;
4409
4410 if (!info->attrs[NL80211_ATTR_MAC] ||
Johannes Berg19957bb2009-07-02 17:20:43 +02004411 !info->attrs[NL80211_ATTR_SSID] ||
4412 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004413 return -EINVAL;
4414
Johannes Berg4c476992010-10-04 21:36:35 +02004415 if (!rdev->ops->assoc)
4416 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004417
Johannes Berg074ac8d2010-09-16 14:58:22 +02004418 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004419 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4420 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004421
Johannes Berg19957bb2009-07-02 17:20:43 +02004422 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004423
Johannes Berg19957bb2009-07-02 17:20:43 +02004424 chan = ieee80211_get_channel(&rdev->wiphy,
4425 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
Johannes Berg4c476992010-10-04 21:36:35 +02004426 if (!chan || (chan->flags & IEEE80211_CHAN_DISABLED))
4427 return -EINVAL;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004428
Johannes Berg19957bb2009-07-02 17:20:43 +02004429 ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4430 ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004431
4432 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004433 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4434 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004435 }
4436
Jouni Malinendc6382c2009-05-06 22:09:37 +03004437 if (info->attrs[NL80211_ATTR_USE_MFP]) {
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004438 enum nl80211_mfp mfp =
Jouni Malinendc6382c2009-05-06 22:09:37 +03004439 nla_get_u32(info->attrs[NL80211_ATTR_USE_MFP]);
Johannes Berg4f5dadc2009-07-07 03:56:10 +02004440 if (mfp == NL80211_MFP_REQUIRED)
Johannes Berg19957bb2009-07-02 17:20:43 +02004441 use_mfp = true;
Johannes Berg4c476992010-10-04 21:36:35 +02004442 else if (mfp != NL80211_MFP_NO)
4443 return -EINVAL;
Jouni Malinendc6382c2009-05-06 22:09:37 +03004444 }
4445
Johannes Berg3e5d7642009-07-07 14:37:26 +02004446 if (info->attrs[NL80211_ATTR_PREV_BSSID])
4447 prev_bssid = nla_data(info->attrs[NL80211_ATTR_PREV_BSSID]);
4448
Johannes Bergc0692b82010-08-27 14:26:53 +03004449 err = nl80211_crypto_settings(rdev, info, &crypto, 1);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004450 if (!err)
Johannes Berg3e5d7642009-07-07 14:37:26 +02004451 err = cfg80211_mlme_assoc(rdev, dev, chan, bssid, prev_bssid,
4452 ssid, ssid_len, ie, ie_len, use_mfp,
Johannes Berg19957bb2009-07-02 17:20:43 +02004453 &crypto);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004454
Jouni Malinen636a5d32009-03-19 13:39:22 +02004455 return err;
4456}
4457
4458static int nl80211_deauthenticate(struct sk_buff *skb, struct genl_info *info)
4459{
Johannes Berg4c476992010-10-04 21:36:35 +02004460 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4461 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004462 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004463 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004464 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004465 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004466
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004467 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4468 return -EINVAL;
4469
4470 if (!info->attrs[NL80211_ATTR_MAC])
4471 return -EINVAL;
4472
4473 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4474 return -EINVAL;
4475
Johannes Berg4c476992010-10-04 21:36:35 +02004476 if (!rdev->ops->deauth)
4477 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004478
Johannes Berg074ac8d2010-09-16 14:58:22 +02004479 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004480 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4481 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004482
Johannes Berg19957bb2009-07-02 17:20:43 +02004483 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004484
Johannes Berg19957bb2009-07-02 17:20:43 +02004485 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4486 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004487 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004488 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004489 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004490
4491 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004492 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4493 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004494 }
4495
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004496 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4497
Johannes Berg4c476992010-10-04 21:36:35 +02004498 return cfg80211_mlme_deauth(rdev, dev, bssid, ie, ie_len, reason_code,
4499 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004500}
4501
4502static int nl80211_disassociate(struct sk_buff *skb, struct genl_info *info)
4503{
Johannes Berg4c476992010-10-04 21:36:35 +02004504 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4505 struct net_device *dev = info->user_ptr[1];
Johannes Berg19957bb2009-07-02 17:20:43 +02004506 const u8 *ie = NULL, *bssid;
Johannes Berg4c476992010-10-04 21:36:35 +02004507 int ie_len = 0;
Johannes Berg19957bb2009-07-02 17:20:43 +02004508 u16 reason_code;
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004509 bool local_state_change;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004510
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004511 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4512 return -EINVAL;
4513
4514 if (!info->attrs[NL80211_ATTR_MAC])
4515 return -EINVAL;
4516
4517 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4518 return -EINVAL;
4519
Johannes Berg4c476992010-10-04 21:36:35 +02004520 if (!rdev->ops->disassoc)
4521 return -EOPNOTSUPP;
Jouni Malinen636a5d32009-03-19 13:39:22 +02004522
Johannes Berg074ac8d2010-09-16 14:58:22 +02004523 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004524 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4525 return -EOPNOTSUPP;
Jouni Malineneec60b02009-03-20 21:21:19 +02004526
Johannes Berg19957bb2009-07-02 17:20:43 +02004527 bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004528
Johannes Berg19957bb2009-07-02 17:20:43 +02004529 reason_code = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4530 if (reason_code == 0) {
Johannes Bergf4a11bb2009-03-27 12:40:28 +01004531 /* Reason Code 0 is reserved */
Johannes Berg4c476992010-10-04 21:36:35 +02004532 return -EINVAL;
Jouni Malinen255e7372009-03-20 21:21:17 +02004533 }
Jouni Malinen636a5d32009-03-19 13:39:22 +02004534
4535 if (info->attrs[NL80211_ATTR_IE]) {
Johannes Berg19957bb2009-07-02 17:20:43 +02004536 ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4537 ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004538 }
4539
Jouni Malinend5cdfac2010-04-04 09:37:19 +03004540 local_state_change = !!info->attrs[NL80211_ATTR_LOCAL_STATE_CHANGE];
4541
Johannes Berg4c476992010-10-04 21:36:35 +02004542 return cfg80211_mlme_disassoc(rdev, dev, bssid, ie, ie_len, reason_code,
4543 local_state_change);
Jouni Malinen636a5d32009-03-19 13:39:22 +02004544}
4545
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004546static bool
4547nl80211_parse_mcast_rate(struct cfg80211_registered_device *rdev,
4548 int mcast_rate[IEEE80211_NUM_BANDS],
4549 int rateval)
4550{
4551 struct wiphy *wiphy = &rdev->wiphy;
4552 bool found = false;
4553 int band, i;
4554
4555 for (band = 0; band < IEEE80211_NUM_BANDS; band++) {
4556 struct ieee80211_supported_band *sband;
4557
4558 sband = wiphy->bands[band];
4559 if (!sband)
4560 continue;
4561
4562 for (i = 0; i < sband->n_bitrates; i++) {
4563 if (sband->bitrates[i].bitrate == rateval) {
4564 mcast_rate[band] = i + 1;
4565 found = true;
4566 break;
4567 }
4568 }
4569 }
4570
4571 return found;
4572}
4573
Johannes Berg04a773a2009-04-19 21:24:32 +02004574static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info)
4575{
Johannes Berg4c476992010-10-04 21:36:35 +02004576 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4577 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004578 struct cfg80211_ibss_params ibss;
4579 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004580 struct cfg80211_cached_keys *connkeys = NULL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004581 int err;
4582
Johannes Berg8e30bc52009-04-22 17:45:38 +02004583 memset(&ibss, 0, sizeof(ibss));
4584
Johannes Berg04a773a2009-04-19 21:24:32 +02004585 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4586 return -EINVAL;
4587
4588 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
4589 !info->attrs[NL80211_ATTR_SSID] ||
4590 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4591 return -EINVAL;
4592
Johannes Berg8e30bc52009-04-22 17:45:38 +02004593 ibss.beacon_interval = 100;
4594
4595 if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) {
4596 ibss.beacon_interval =
4597 nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]);
4598 if (ibss.beacon_interval < 1 || ibss.beacon_interval > 10000)
4599 return -EINVAL;
4600 }
4601
Johannes Berg4c476992010-10-04 21:36:35 +02004602 if (!rdev->ops->join_ibss)
4603 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004604
Johannes Berg4c476992010-10-04 21:36:35 +02004605 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4606 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004607
Johannes Berg79c97e92009-07-07 03:56:12 +02004608 wiphy = &rdev->wiphy;
Johannes Berg04a773a2009-04-19 21:24:32 +02004609
Johannes Berg39193492011-09-16 13:45:25 +02004610 if (info->attrs[NL80211_ATTR_MAC]) {
Johannes Berg04a773a2009-04-19 21:24:32 +02004611 ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
Johannes Berg39193492011-09-16 13:45:25 +02004612
4613 if (!is_valid_ether_addr(ibss.bssid))
4614 return -EINVAL;
4615 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004616 ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4617 ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4618
4619 if (info->attrs[NL80211_ATTR_IE]) {
4620 ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4621 ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4622 }
4623
4624 ibss.channel = ieee80211_get_channel(wiphy,
4625 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4626 if (!ibss.channel ||
4627 ibss.channel->flags & IEEE80211_CHAN_NO_IBSS ||
Johannes Berg4c476992010-10-04 21:36:35 +02004628 ibss.channel->flags & IEEE80211_CHAN_DISABLED)
4629 return -EINVAL;
Johannes Berg04a773a2009-04-19 21:24:32 +02004630
4631 ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED];
Johannes Bergfffd0932009-07-08 14:22:54 +02004632 ibss.privacy = !!info->attrs[NL80211_ATTR_PRIVACY];
Johannes Berg04a773a2009-04-19 21:24:32 +02004633
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004634 if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) {
4635 u8 *rates =
4636 nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4637 int n_rates =
4638 nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]);
4639 struct ieee80211_supported_band *sband =
4640 wiphy->bands[ibss.channel->band];
Johannes Berg34850ab2011-07-18 18:08:35 +02004641 int err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004642
Johannes Berg34850ab2011-07-18 18:08:35 +02004643 err = ieee80211_get_ratemask(sband, rates, n_rates,
4644 &ibss.basic_rates);
4645 if (err)
4646 return err;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004647 }
Felix Fietkaudd5b4cc2010-11-22 20:58:24 +01004648
4649 if (info->attrs[NL80211_ATTR_MCAST_RATE] &&
4650 !nl80211_parse_mcast_rate(rdev, ibss.mcast_rate,
4651 nla_get_u32(info->attrs[NL80211_ATTR_MCAST_RATE])))
4652 return -EINVAL;
Teemu Paasikivifbd2c8d2010-06-14 12:55:31 +03004653
Johannes Berg4c476992010-10-04 21:36:35 +02004654 if (ibss.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4655 connkeys = nl80211_parse_connkeys(rdev,
4656 info->attrs[NL80211_ATTR_KEYS]);
4657 if (IS_ERR(connkeys))
4658 return PTR_ERR(connkeys);
4659 }
Johannes Berg04a773a2009-04-19 21:24:32 +02004660
Johannes Berg4c476992010-10-04 21:36:35 +02004661 err = cfg80211_join_ibss(rdev, dev, &ibss, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004662 if (err)
4663 kfree(connkeys);
Johannes Berg04a773a2009-04-19 21:24:32 +02004664 return err;
4665}
4666
4667static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info)
4668{
Johannes Berg4c476992010-10-04 21:36:35 +02004669 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4670 struct net_device *dev = info->user_ptr[1];
Johannes Berg04a773a2009-04-19 21:24:32 +02004671
Johannes Berg4c476992010-10-04 21:36:35 +02004672 if (!rdev->ops->leave_ibss)
4673 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004674
Johannes Berg4c476992010-10-04 21:36:35 +02004675 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC)
4676 return -EOPNOTSUPP;
Johannes Berg04a773a2009-04-19 21:24:32 +02004677
Johannes Berg4c476992010-10-04 21:36:35 +02004678 return cfg80211_leave_ibss(rdev, dev, false);
Johannes Berg04a773a2009-04-19 21:24:32 +02004679}
4680
Johannes Bergaff89a92009-07-01 21:26:51 +02004681#ifdef CONFIG_NL80211_TESTMODE
4682static struct genl_multicast_group nl80211_testmode_mcgrp = {
4683 .name = "testmode",
4684};
4685
4686static int nl80211_testmode_do(struct sk_buff *skb, struct genl_info *info)
4687{
Johannes Berg4c476992010-10-04 21:36:35 +02004688 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Bergaff89a92009-07-01 21:26:51 +02004689 int err;
4690
4691 if (!info->attrs[NL80211_ATTR_TESTDATA])
4692 return -EINVAL;
4693
Johannes Bergaff89a92009-07-01 21:26:51 +02004694 err = -EOPNOTSUPP;
4695 if (rdev->ops->testmode_cmd) {
4696 rdev->testmode_info = info;
4697 err = rdev->ops->testmode_cmd(&rdev->wiphy,
4698 nla_data(info->attrs[NL80211_ATTR_TESTDATA]),
4699 nla_len(info->attrs[NL80211_ATTR_TESTDATA]));
4700 rdev->testmode_info = NULL;
4701 }
4702
Johannes Bergaff89a92009-07-01 21:26:51 +02004703 return err;
4704}
4705
Wey-Yi Guy71063f02011-05-20 09:05:54 -07004706static int nl80211_testmode_dump(struct sk_buff *skb,
4707 struct netlink_callback *cb)
4708{
4709 struct cfg80211_registered_device *dev;
4710 int err;
4711 long phy_idx;
4712 void *data = NULL;
4713 int data_len = 0;
4714
4715 if (cb->args[0]) {
4716 /*
4717 * 0 is a valid index, but not valid for args[0],
4718 * so we need to offset by 1.
4719 */
4720 phy_idx = cb->args[0] - 1;
4721 } else {
4722 err = nlmsg_parse(cb->nlh, GENL_HDRLEN + nl80211_fam.hdrsize,
4723 nl80211_fam.attrbuf, nl80211_fam.maxattr,
4724 nl80211_policy);
4725 if (err)
4726 return err;
4727 if (!nl80211_fam.attrbuf[NL80211_ATTR_WIPHY])
4728 return -EINVAL;
4729 phy_idx = nla_get_u32(nl80211_fam.attrbuf[NL80211_ATTR_WIPHY]);
4730 if (nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA])
4731 cb->args[1] =
4732 (long)nl80211_fam.attrbuf[NL80211_ATTR_TESTDATA];
4733 }
4734
4735 if (cb->args[1]) {
4736 data = nla_data((void *)cb->args[1]);
4737 data_len = nla_len((void *)cb->args[1]);
4738 }
4739
4740 mutex_lock(&cfg80211_mutex);
4741 dev = cfg80211_rdev_by_wiphy_idx(phy_idx);
4742 if (!dev) {
4743 mutex_unlock(&cfg80211_mutex);
4744 return -ENOENT;
4745 }
4746 cfg80211_lock_rdev(dev);
4747 mutex_unlock(&cfg80211_mutex);
4748
4749 if (!dev->ops->testmode_dump) {
4750 err = -EOPNOTSUPP;
4751 goto out_err;
4752 }
4753
4754 while (1) {
4755 void *hdr = nl80211hdr_put(skb, NETLINK_CB(cb->skb).pid,
4756 cb->nlh->nlmsg_seq, NLM_F_MULTI,
4757 NL80211_CMD_TESTMODE);
4758 struct nlattr *tmdata;
4759
4760 if (nla_put_u32(skb, NL80211_ATTR_WIPHY, dev->wiphy_idx) < 0) {
4761 genlmsg_cancel(skb, hdr);
4762 break;
4763 }
4764
4765 tmdata = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4766 if (!tmdata) {
4767 genlmsg_cancel(skb, hdr);
4768 break;
4769 }
4770 err = dev->ops->testmode_dump(&dev->wiphy, skb, cb,
4771 data, data_len);
4772 nla_nest_end(skb, tmdata);
4773
4774 if (err == -ENOBUFS || err == -ENOENT) {
4775 genlmsg_cancel(skb, hdr);
4776 break;
4777 } else if (err) {
4778 genlmsg_cancel(skb, hdr);
4779 goto out_err;
4780 }
4781
4782 genlmsg_end(skb, hdr);
4783 }
4784
4785 err = skb->len;
4786 /* see above */
4787 cb->args[0] = phy_idx + 1;
4788 out_err:
4789 cfg80211_unlock_rdev(dev);
4790 return err;
4791}
4792
Johannes Bergaff89a92009-07-01 21:26:51 +02004793static struct sk_buff *
4794__cfg80211_testmode_alloc_skb(struct cfg80211_registered_device *rdev,
4795 int approxlen, u32 pid, u32 seq, gfp_t gfp)
4796{
4797 struct sk_buff *skb;
4798 void *hdr;
4799 struct nlattr *data;
4800
4801 skb = nlmsg_new(approxlen + 100, gfp);
4802 if (!skb)
4803 return NULL;
4804
4805 hdr = nl80211hdr_put(skb, pid, seq, 0, NL80211_CMD_TESTMODE);
4806 if (!hdr) {
4807 kfree_skb(skb);
4808 return NULL;
4809 }
4810
4811 NLA_PUT_U32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
4812 data = nla_nest_start(skb, NL80211_ATTR_TESTDATA);
4813
4814 ((void **)skb->cb)[0] = rdev;
4815 ((void **)skb->cb)[1] = hdr;
4816 ((void **)skb->cb)[2] = data;
4817
4818 return skb;
4819
4820 nla_put_failure:
4821 kfree_skb(skb);
4822 return NULL;
4823}
4824
4825struct sk_buff *cfg80211_testmode_alloc_reply_skb(struct wiphy *wiphy,
4826 int approxlen)
4827{
4828 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4829
4830 if (WARN_ON(!rdev->testmode_info))
4831 return NULL;
4832
4833 return __cfg80211_testmode_alloc_skb(rdev, approxlen,
4834 rdev->testmode_info->snd_pid,
4835 rdev->testmode_info->snd_seq,
4836 GFP_KERNEL);
4837}
4838EXPORT_SYMBOL(cfg80211_testmode_alloc_reply_skb);
4839
4840int cfg80211_testmode_reply(struct sk_buff *skb)
4841{
4842 struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0];
4843 void *hdr = ((void **)skb->cb)[1];
4844 struct nlattr *data = ((void **)skb->cb)[2];
4845
4846 if (WARN_ON(!rdev->testmode_info)) {
4847 kfree_skb(skb);
4848 return -EINVAL;
4849 }
4850
4851 nla_nest_end(skb, data);
4852 genlmsg_end(skb, hdr);
4853 return genlmsg_reply(skb, rdev->testmode_info);
4854}
4855EXPORT_SYMBOL(cfg80211_testmode_reply);
4856
4857struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy,
4858 int approxlen, gfp_t gfp)
4859{
4860 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
4861
4862 return __cfg80211_testmode_alloc_skb(rdev, approxlen, 0, 0, gfp);
4863}
4864EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb);
4865
4866void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp)
4867{
4868 void *hdr = ((void **)skb->cb)[1];
4869 struct nlattr *data = ((void **)skb->cb)[2];
4870
4871 nla_nest_end(skb, data);
4872 genlmsg_end(skb, hdr);
4873 genlmsg_multicast(skb, 0, nl80211_testmode_mcgrp.id, gfp);
4874}
4875EXPORT_SYMBOL(cfg80211_testmode_event);
4876#endif
4877
Samuel Ortizb23aa672009-07-01 21:26:54 +02004878static int nl80211_connect(struct sk_buff *skb, struct genl_info *info)
4879{
Johannes Berg4c476992010-10-04 21:36:35 +02004880 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4881 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004882 struct cfg80211_connect_params connect;
4883 struct wiphy *wiphy;
Johannes Bergfffd0932009-07-08 14:22:54 +02004884 struct cfg80211_cached_keys *connkeys = NULL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004885 int err;
4886
4887 memset(&connect, 0, sizeof(connect));
4888
4889 if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE]))
4890 return -EINVAL;
4891
4892 if (!info->attrs[NL80211_ATTR_SSID] ||
4893 !nla_len(info->attrs[NL80211_ATTR_SSID]))
4894 return -EINVAL;
4895
4896 if (info->attrs[NL80211_ATTR_AUTH_TYPE]) {
4897 connect.auth_type =
4898 nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]);
4899 if (!nl80211_valid_auth_type(connect.auth_type))
4900 return -EINVAL;
4901 } else
4902 connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC;
4903
4904 connect.privacy = info->attrs[NL80211_ATTR_PRIVACY];
4905
Johannes Bergc0692b82010-08-27 14:26:53 +03004906 err = nl80211_crypto_settings(rdev, info, &connect.crypto,
Johannes Berg3dc27d22009-07-02 21:36:37 +02004907 NL80211_MAX_NR_CIPHER_SUITES);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004908 if (err)
4909 return err;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004910
Johannes Berg074ac8d2010-09-16 14:58:22 +02004911 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004912 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4913 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004914
Johannes Berg79c97e92009-07-07 03:56:12 +02004915 wiphy = &rdev->wiphy;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004916
Samuel Ortizb23aa672009-07-01 21:26:54 +02004917 if (info->attrs[NL80211_ATTR_MAC])
4918 connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
4919 connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]);
4920 connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]);
4921
4922 if (info->attrs[NL80211_ATTR_IE]) {
4923 connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]);
4924 connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]);
4925 }
4926
4927 if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) {
4928 connect.channel =
4929 ieee80211_get_channel(wiphy,
4930 nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]));
4931 if (!connect.channel ||
Johannes Berg4c476992010-10-04 21:36:35 +02004932 connect.channel->flags & IEEE80211_CHAN_DISABLED)
4933 return -EINVAL;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004934 }
4935
Johannes Bergfffd0932009-07-08 14:22:54 +02004936 if (connect.privacy && info->attrs[NL80211_ATTR_KEYS]) {
4937 connkeys = nl80211_parse_connkeys(rdev,
4938 info->attrs[NL80211_ATTR_KEYS]);
Johannes Berg4c476992010-10-04 21:36:35 +02004939 if (IS_ERR(connkeys))
4940 return PTR_ERR(connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004941 }
4942
4943 err = cfg80211_connect(rdev, dev, &connect, connkeys);
Johannes Bergfffd0932009-07-08 14:22:54 +02004944 if (err)
4945 kfree(connkeys);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004946 return err;
4947}
4948
4949static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info)
4950{
Johannes Berg4c476992010-10-04 21:36:35 +02004951 struct cfg80211_registered_device *rdev = info->user_ptr[0];
4952 struct net_device *dev = info->user_ptr[1];
Samuel Ortizb23aa672009-07-01 21:26:54 +02004953 u16 reason;
4954
4955 if (!info->attrs[NL80211_ATTR_REASON_CODE])
4956 reason = WLAN_REASON_DEAUTH_LEAVING;
4957 else
4958 reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]);
4959
4960 if (reason == 0)
4961 return -EINVAL;
4962
Johannes Berg074ac8d2010-09-16 14:58:22 +02004963 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02004964 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
4965 return -EOPNOTSUPP;
Samuel Ortizb23aa672009-07-01 21:26:54 +02004966
Johannes Berg4c476992010-10-04 21:36:35 +02004967 return cfg80211_disconnect(rdev, dev, reason, true);
Samuel Ortizb23aa672009-07-01 21:26:54 +02004968}
4969
Johannes Berg463d0182009-07-14 00:33:35 +02004970static int nl80211_wiphy_netns(struct sk_buff *skb, struct genl_info *info)
4971{
Johannes Berg4c476992010-10-04 21:36:35 +02004972 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Johannes Berg463d0182009-07-14 00:33:35 +02004973 struct net *net;
4974 int err;
4975 u32 pid;
4976
4977 if (!info->attrs[NL80211_ATTR_PID])
4978 return -EINVAL;
4979
4980 pid = nla_get_u32(info->attrs[NL80211_ATTR_PID]);
4981
Johannes Berg463d0182009-07-14 00:33:35 +02004982 net = get_net_ns_by_pid(pid);
Johannes Berg4c476992010-10-04 21:36:35 +02004983 if (IS_ERR(net))
4984 return PTR_ERR(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004985
4986 err = 0;
4987
4988 /* check if anything to do */
Johannes Berg4c476992010-10-04 21:36:35 +02004989 if (!net_eq(wiphy_net(&rdev->wiphy), net))
4990 err = cfg80211_switch_netns(rdev, net);
Johannes Berg463d0182009-07-14 00:33:35 +02004991
Johannes Berg463d0182009-07-14 00:33:35 +02004992 put_net(net);
Johannes Berg463d0182009-07-14 00:33:35 +02004993 return err;
4994}
4995
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004996static int nl80211_setdel_pmksa(struct sk_buff *skb, struct genl_info *info)
4997{
Johannes Berg4c476992010-10-04 21:36:35 +02004998 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01004999 int (*rdev_ops)(struct wiphy *wiphy, struct net_device *dev,
5000 struct cfg80211_pmksa *pmksa) = NULL;
Johannes Berg4c476992010-10-04 21:36:35 +02005001 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005002 struct cfg80211_pmksa pmksa;
5003
5004 memset(&pmksa, 0, sizeof(struct cfg80211_pmksa));
5005
5006 if (!info->attrs[NL80211_ATTR_MAC])
5007 return -EINVAL;
5008
5009 if (!info->attrs[NL80211_ATTR_PMKID])
5010 return -EINVAL;
5011
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005012 pmksa.pmkid = nla_data(info->attrs[NL80211_ATTR_PMKID]);
5013 pmksa.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]);
5014
Johannes Berg074ac8d2010-09-16 14:58:22 +02005015 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005016 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5017 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005018
5019 switch (info->genlhdr->cmd) {
5020 case NL80211_CMD_SET_PMKSA:
5021 rdev_ops = rdev->ops->set_pmksa;
5022 break;
5023 case NL80211_CMD_DEL_PMKSA:
5024 rdev_ops = rdev->ops->del_pmksa;
5025 break;
5026 default:
5027 WARN_ON(1);
5028 break;
5029 }
5030
Johannes Berg4c476992010-10-04 21:36:35 +02005031 if (!rdev_ops)
5032 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005033
Johannes Berg4c476992010-10-04 21:36:35 +02005034 return rdev_ops(&rdev->wiphy, dev, &pmksa);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005035}
5036
5037static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info)
5038{
Johannes Berg4c476992010-10-04 21:36:35 +02005039 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5040 struct net_device *dev = info->user_ptr[1];
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005041
Johannes Berg074ac8d2010-09-16 14:58:22 +02005042 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005043 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT)
5044 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005045
Johannes Berg4c476992010-10-04 21:36:35 +02005046 if (!rdev->ops->flush_pmksa)
5047 return -EOPNOTSUPP;
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005048
Johannes Berg4c476992010-10-04 21:36:35 +02005049 return rdev->ops->flush_pmksa(&rdev->wiphy, dev);
Samuel Ortiz67fbb162009-11-24 23:59:15 +01005050}
5051
Arik Nemtsov109086c2011-09-28 14:12:50 +03005052static int nl80211_tdls_mgmt(struct sk_buff *skb, struct genl_info *info)
5053{
5054 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5055 struct net_device *dev = info->user_ptr[1];
5056 u8 action_code, dialog_token;
5057 u16 status_code;
5058 u8 *peer;
5059
5060 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5061 !rdev->ops->tdls_mgmt)
5062 return -EOPNOTSUPP;
5063
5064 if (!info->attrs[NL80211_ATTR_TDLS_ACTION] ||
5065 !info->attrs[NL80211_ATTR_STATUS_CODE] ||
5066 !info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN] ||
5067 !info->attrs[NL80211_ATTR_IE] ||
5068 !info->attrs[NL80211_ATTR_MAC])
5069 return -EINVAL;
5070
5071 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5072 action_code = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_ACTION]);
5073 status_code = nla_get_u16(info->attrs[NL80211_ATTR_STATUS_CODE]);
5074 dialog_token = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_DIALOG_TOKEN]);
5075
5076 return rdev->ops->tdls_mgmt(&rdev->wiphy, dev, peer, action_code,
5077 dialog_token, status_code,
5078 nla_data(info->attrs[NL80211_ATTR_IE]),
5079 nla_len(info->attrs[NL80211_ATTR_IE]));
5080}
5081
5082static int nl80211_tdls_oper(struct sk_buff *skb, struct genl_info *info)
5083{
5084 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5085 struct net_device *dev = info->user_ptr[1];
5086 enum nl80211_tdls_operation operation;
5087 u8 *peer;
5088
5089 if (!(rdev->wiphy.flags & WIPHY_FLAG_SUPPORTS_TDLS) ||
5090 !rdev->ops->tdls_oper)
5091 return -EOPNOTSUPP;
5092
5093 if (!info->attrs[NL80211_ATTR_TDLS_OPERATION] ||
5094 !info->attrs[NL80211_ATTR_MAC])
5095 return -EINVAL;
5096
5097 operation = nla_get_u8(info->attrs[NL80211_ATTR_TDLS_OPERATION]);
5098 peer = nla_data(info->attrs[NL80211_ATTR_MAC]);
5099
5100 return rdev->ops->tdls_oper(&rdev->wiphy, dev, peer, operation);
5101}
5102
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005103static int nl80211_remain_on_channel(struct sk_buff *skb,
5104 struct genl_info *info)
5105{
Johannes Berg4c476992010-10-04 21:36:35 +02005106 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5107 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005108 struct ieee80211_channel *chan;
5109 struct sk_buff *msg;
5110 void *hdr;
5111 u64 cookie;
5112 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
5113 u32 freq, duration;
5114 int err;
5115
5116 if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] ||
5117 !info->attrs[NL80211_ATTR_DURATION])
5118 return -EINVAL;
5119
5120 duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5121
5122 /*
5123 * We should be on that channel for at least one jiffie,
5124 * and more than 5 seconds seems excessive.
5125 */
Johannes Berga2939112010-12-14 17:54:28 +01005126 if (!duration || !msecs_to_jiffies(duration) ||
5127 duration > rdev->wiphy.max_remain_on_channel_duration)
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005128 return -EINVAL;
5129
Johannes Berg4c476992010-10-04 21:36:35 +02005130 if (!rdev->ops->remain_on_channel)
5131 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005132
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005133 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5134 channel_type = nla_get_u32(
5135 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5136 if (channel_type != NL80211_CHAN_NO_HT &&
5137 channel_type != NL80211_CHAN_HT20 &&
5138 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005139 channel_type != NL80211_CHAN_HT40MINUS)
5140 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005141 }
5142
5143 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5144 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005145 if (chan == NULL)
5146 return -EINVAL;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005147
5148 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005149 if (!msg)
5150 return -ENOMEM;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005151
5152 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5153 NL80211_CMD_REMAIN_ON_CHANNEL);
5154
5155 if (IS_ERR(hdr)) {
5156 err = PTR_ERR(hdr);
5157 goto free_msg;
5158 }
5159
5160 err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan,
5161 channel_type, duration, &cookie);
5162
5163 if (err)
5164 goto free_msg;
5165
5166 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5167
5168 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005169
5170 return genlmsg_reply(msg, info);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005171
5172 nla_put_failure:
5173 err = -ENOBUFS;
5174 free_msg:
5175 nlmsg_free(msg);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005176 return err;
5177}
5178
5179static int nl80211_cancel_remain_on_channel(struct sk_buff *skb,
5180 struct genl_info *info)
5181{
Johannes Berg4c476992010-10-04 21:36:35 +02005182 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5183 struct net_device *dev = info->user_ptr[1];
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005184 u64 cookie;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005185
5186 if (!info->attrs[NL80211_ATTR_COOKIE])
5187 return -EINVAL;
5188
Johannes Berg4c476992010-10-04 21:36:35 +02005189 if (!rdev->ops->cancel_remain_on_channel)
5190 return -EOPNOTSUPP;
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005191
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005192 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5193
Johannes Berg4c476992010-10-04 21:36:35 +02005194 return rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01005195}
5196
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005197static u32 rateset_to_mask(struct ieee80211_supported_band *sband,
5198 u8 *rates, u8 rates_len)
5199{
5200 u8 i;
5201 u32 mask = 0;
5202
5203 for (i = 0; i < rates_len; i++) {
5204 int rate = (rates[i] & 0x7f) * 5;
5205 int ridx;
5206 for (ridx = 0; ridx < sband->n_bitrates; ridx++) {
5207 struct ieee80211_rate *srate =
5208 &sband->bitrates[ridx];
5209 if (rate == srate->bitrate) {
5210 mask |= 1 << ridx;
5211 break;
5212 }
5213 }
5214 if (ridx == sband->n_bitrates)
5215 return 0; /* rate not found */
5216 }
5217
5218 return mask;
5219}
5220
Alexey Dobriyanb54452b2010-02-18 08:14:31 +00005221static const struct nla_policy nl80211_txattr_policy[NL80211_TXRATE_MAX + 1] = {
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005222 [NL80211_TXRATE_LEGACY] = { .type = NLA_BINARY,
5223 .len = NL80211_MAX_SUPP_RATES },
5224};
5225
5226static int nl80211_set_tx_bitrate_mask(struct sk_buff *skb,
5227 struct genl_info *info)
5228{
5229 struct nlattr *tb[NL80211_TXRATE_MAX + 1];
Johannes Berg4c476992010-10-04 21:36:35 +02005230 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005231 struct cfg80211_bitrate_mask mask;
Johannes Berg4c476992010-10-04 21:36:35 +02005232 int rem, i;
5233 struct net_device *dev = info->user_ptr[1];
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005234 struct nlattr *tx_rates;
5235 struct ieee80211_supported_band *sband;
5236
5237 if (info->attrs[NL80211_ATTR_TX_RATES] == NULL)
5238 return -EINVAL;
5239
Johannes Berg4c476992010-10-04 21:36:35 +02005240 if (!rdev->ops->set_bitrate_mask)
5241 return -EOPNOTSUPP;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005242
5243 memset(&mask, 0, sizeof(mask));
5244 /* Default to all rates enabled */
5245 for (i = 0; i < IEEE80211_NUM_BANDS; i++) {
5246 sband = rdev->wiphy.bands[i];
5247 mask.control[i].legacy =
5248 sband ? (1 << sband->n_bitrates) - 1 : 0;
5249 }
5250
5251 /*
5252 * The nested attribute uses enum nl80211_band as the index. This maps
5253 * directly to the enum ieee80211_band values used in cfg80211.
5254 */
5255 nla_for_each_nested(tx_rates, info->attrs[NL80211_ATTR_TX_RATES], rem)
5256 {
5257 enum ieee80211_band band = nla_type(tx_rates);
Johannes Berg4c476992010-10-04 21:36:35 +02005258 if (band < 0 || band >= IEEE80211_NUM_BANDS)
5259 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005260 sband = rdev->wiphy.bands[band];
Johannes Berg4c476992010-10-04 21:36:35 +02005261 if (sband == NULL)
5262 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005263 nla_parse(tb, NL80211_TXRATE_MAX, nla_data(tx_rates),
5264 nla_len(tx_rates), nl80211_txattr_policy);
5265 if (tb[NL80211_TXRATE_LEGACY]) {
5266 mask.control[band].legacy = rateset_to_mask(
5267 sband,
5268 nla_data(tb[NL80211_TXRATE_LEGACY]),
5269 nla_len(tb[NL80211_TXRATE_LEGACY]));
Johannes Berg4c476992010-10-04 21:36:35 +02005270 if (mask.control[band].legacy == 0)
5271 return -EINVAL;
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005272 }
5273 }
5274
Johannes Berg4c476992010-10-04 21:36:35 +02005275 return rdev->ops->set_bitrate_mask(&rdev->wiphy, dev, NULL, &mask);
Jouni Malinen13ae75b2009-12-29 12:59:45 +02005276}
5277
Johannes Berg2e161f72010-08-12 15:38:38 +02005278static int nl80211_register_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005279{
Johannes Berg4c476992010-10-04 21:36:35 +02005280 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5281 struct net_device *dev = info->user_ptr[1];
Johannes Berg2e161f72010-08-12 15:38:38 +02005282 u16 frame_type = IEEE80211_FTYPE_MGMT | IEEE80211_STYPE_ACTION;
Jouni Malinen026331c2010-02-15 12:53:10 +02005283
5284 if (!info->attrs[NL80211_ATTR_FRAME_MATCH])
5285 return -EINVAL;
5286
Johannes Berg2e161f72010-08-12 15:38:38 +02005287 if (info->attrs[NL80211_ATTR_FRAME_TYPE])
5288 frame_type = nla_get_u16(info->attrs[NL80211_ATTR_FRAME_TYPE]);
Jouni Malinen026331c2010-02-15 12:53:10 +02005289
Johannes Berg9d38d852010-06-09 17:20:33 +02005290 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005291 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005292 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5293 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5294 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005295 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005296 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5297 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005298
5299 /* not much point in registering if we can't reply */
Johannes Berg4c476992010-10-04 21:36:35 +02005300 if (!rdev->ops->mgmt_tx)
5301 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005302
Johannes Berg4c476992010-10-04 21:36:35 +02005303 return cfg80211_mlme_register_mgmt(dev->ieee80211_ptr, info->snd_pid,
Johannes Berg2e161f72010-08-12 15:38:38 +02005304 frame_type,
Jouni Malinen026331c2010-02-15 12:53:10 +02005305 nla_data(info->attrs[NL80211_ATTR_FRAME_MATCH]),
5306 nla_len(info->attrs[NL80211_ATTR_FRAME_MATCH]));
Jouni Malinen026331c2010-02-15 12:53:10 +02005307}
5308
Johannes Berg2e161f72010-08-12 15:38:38 +02005309static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
Jouni Malinen026331c2010-02-15 12:53:10 +02005310{
Johannes Berg4c476992010-10-04 21:36:35 +02005311 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5312 struct net_device *dev = info->user_ptr[1];
Jouni Malinen026331c2010-02-15 12:53:10 +02005313 struct ieee80211_channel *chan;
5314 enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT;
Johannes Berg252aa632010-05-19 12:17:12 +02005315 bool channel_type_valid = false;
Jouni Malinen026331c2010-02-15 12:53:10 +02005316 u32 freq;
5317 int err;
Johannes Bergd64d3732011-11-10 09:44:46 +01005318 void *hdr = NULL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005319 u64 cookie;
Johannes Berge247bd902011-11-04 11:18:21 +01005320 struct sk_buff *msg = NULL;
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005321 unsigned int wait = 0;
Johannes Berge247bd902011-11-04 11:18:21 +01005322 bool offchan, no_cck, dont_wait_for_ack;
5323
5324 dont_wait_for_ack = info->attrs[NL80211_ATTR_DONT_WAIT_FOR_ACK];
Jouni Malinen026331c2010-02-15 12:53:10 +02005325
5326 if (!info->attrs[NL80211_ATTR_FRAME] ||
5327 !info->attrs[NL80211_ATTR_WIPHY_FREQ])
5328 return -EINVAL;
5329
Johannes Berg4c476992010-10-04 21:36:35 +02005330 if (!rdev->ops->mgmt_tx)
5331 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005332
Johannes Berg9d38d852010-06-09 17:20:33 +02005333 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg074ac8d2010-09-16 14:58:22 +02005334 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
Johannes Berg663fcaf2010-09-30 21:06:09 +02005335 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5336 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5337 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
Javier Cardonac7108a72010-12-16 17:37:50 -08005338 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_MESH_POINT &&
Johannes Berg4c476992010-10-04 21:36:35 +02005339 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5340 return -EOPNOTSUPP;
Jouni Malinen026331c2010-02-15 12:53:10 +02005341
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005342 if (info->attrs[NL80211_ATTR_DURATION]) {
5343 if (!rdev->ops->mgmt_tx_cancel_wait)
5344 return -EINVAL;
5345 wait = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]);
5346 }
5347
Jouni Malinen026331c2010-02-15 12:53:10 +02005348 if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) {
5349 channel_type = nla_get_u32(
5350 info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]);
5351 if (channel_type != NL80211_CHAN_NO_HT &&
5352 channel_type != NL80211_CHAN_HT20 &&
5353 channel_type != NL80211_CHAN_HT40PLUS &&
Johannes Berg4c476992010-10-04 21:36:35 +02005354 channel_type != NL80211_CHAN_HT40MINUS)
5355 return -EINVAL;
Johannes Berg252aa632010-05-19 12:17:12 +02005356 channel_type_valid = true;
Jouni Malinen026331c2010-02-15 12:53:10 +02005357 }
5358
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005359 offchan = info->attrs[NL80211_ATTR_OFFCHANNEL_TX_OK];
5360
Rajkumar Manoharane9f935e2011-09-25 14:53:30 +05305361 no_cck = nla_get_flag(info->attrs[NL80211_ATTR_TX_NO_CCK_RATE]);
5362
Jouni Malinen026331c2010-02-15 12:53:10 +02005363 freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]);
5364 chan = rdev_freq_to_chan(rdev, freq, channel_type);
Johannes Berg4c476992010-10-04 21:36:35 +02005365 if (chan == NULL)
5366 return -EINVAL;
Jouni Malinen026331c2010-02-15 12:53:10 +02005367
Johannes Berge247bd902011-11-04 11:18:21 +01005368 if (!dont_wait_for_ack) {
5369 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5370 if (!msg)
5371 return -ENOMEM;
Jouni Malinen026331c2010-02-15 12:53:10 +02005372
Johannes Berge247bd902011-11-04 11:18:21 +01005373 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5374 NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02005375
Johannes Berge247bd902011-11-04 11:18:21 +01005376 if (IS_ERR(hdr)) {
5377 err = PTR_ERR(hdr);
5378 goto free_msg;
5379 }
Jouni Malinen026331c2010-02-15 12:53:10 +02005380 }
Johannes Berge247bd902011-11-04 11:18:21 +01005381
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005382 err = cfg80211_mlme_mgmt_tx(rdev, dev, chan, offchan, channel_type,
5383 channel_type_valid, wait,
Johannes Berg2e161f72010-08-12 15:38:38 +02005384 nla_data(info->attrs[NL80211_ATTR_FRAME]),
5385 nla_len(info->attrs[NL80211_ATTR_FRAME]),
Johannes Berge247bd902011-11-04 11:18:21 +01005386 no_cck, dont_wait_for_ack, &cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005387 if (err)
5388 goto free_msg;
5389
Johannes Berge247bd902011-11-04 11:18:21 +01005390 if (msg) {
5391 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
Jouni Malinen026331c2010-02-15 12:53:10 +02005392
Johannes Berge247bd902011-11-04 11:18:21 +01005393 genlmsg_end(msg, hdr);
5394 return genlmsg_reply(msg, info);
5395 }
5396
5397 return 0;
Jouni Malinen026331c2010-02-15 12:53:10 +02005398
5399 nla_put_failure:
5400 err = -ENOBUFS;
5401 free_msg:
5402 nlmsg_free(msg);
Jouni Malinen026331c2010-02-15 12:53:10 +02005403 return err;
5404}
5405
Johannes Bergf7ca38d2010-11-25 10:02:29 +01005406static int nl80211_tx_mgmt_cancel_wait(struct sk_buff *skb, struct genl_info *info)
5407{
5408 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5409 struct net_device *dev = info->user_ptr[1];
5410 u64 cookie;
5411
5412 if (!info->attrs[NL80211_ATTR_COOKIE])
5413 return -EINVAL;
5414
5415 if (!rdev->ops->mgmt_tx_cancel_wait)
5416 return -EOPNOTSUPP;
5417
5418 if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION &&
5419 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC &&
5420 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_CLIENT &&
5421 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP &&
5422 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_AP_VLAN &&
5423 dev->ieee80211_ptr->iftype != NL80211_IFTYPE_P2P_GO)
5424 return -EOPNOTSUPP;
5425
5426 cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]);
5427
5428 return rdev->ops->mgmt_tx_cancel_wait(&rdev->wiphy, dev, cookie);
5429}
5430
Kalle Valoffb9eb32010-02-17 17:58:10 +02005431static int nl80211_set_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 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005435 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005436 u8 ps_state;
5437 bool state;
5438 int err;
5439
Johannes Berg4c476992010-10-04 21:36:35 +02005440 if (!info->attrs[NL80211_ATTR_PS_STATE])
5441 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005442
5443 ps_state = nla_get_u32(info->attrs[NL80211_ATTR_PS_STATE]);
5444
Johannes Berg4c476992010-10-04 21:36:35 +02005445 if (ps_state != NL80211_PS_DISABLED && ps_state != NL80211_PS_ENABLED)
5446 return -EINVAL;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005447
5448 wdev = dev->ieee80211_ptr;
5449
Johannes Berg4c476992010-10-04 21:36:35 +02005450 if (!rdev->ops->set_power_mgmt)
5451 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005452
5453 state = (ps_state == NL80211_PS_ENABLED) ? true : false;
5454
5455 if (state == wdev->ps)
Johannes Berg4c476992010-10-04 21:36:35 +02005456 return 0;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005457
Johannes Berg4c476992010-10-04 21:36:35 +02005458 err = rdev->ops->set_power_mgmt(wdev->wiphy, dev, state,
5459 wdev->ps_timeout);
5460 if (!err)
5461 wdev->ps = state;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005462 return err;
5463}
5464
5465static int nl80211_get_power_save(struct sk_buff *skb, struct genl_info *info)
5466{
Johannes Berg4c476992010-10-04 21:36:35 +02005467 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005468 enum nl80211_ps_state ps_state;
5469 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005470 struct net_device *dev = info->user_ptr[1];
Kalle Valoffb9eb32010-02-17 17:58:10 +02005471 struct sk_buff *msg;
5472 void *hdr;
5473 int err;
5474
Kalle Valoffb9eb32010-02-17 17:58:10 +02005475 wdev = dev->ieee80211_ptr;
5476
Johannes Berg4c476992010-10-04 21:36:35 +02005477 if (!rdev->ops->set_power_mgmt)
5478 return -EOPNOTSUPP;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005479
5480 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg4c476992010-10-04 21:36:35 +02005481 if (!msg)
5482 return -ENOMEM;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005483
5484 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5485 NL80211_CMD_GET_POWER_SAVE);
5486 if (!hdr) {
Johannes Berg4c476992010-10-04 21:36:35 +02005487 err = -ENOBUFS;
Kalle Valoffb9eb32010-02-17 17:58:10 +02005488 goto free_msg;
5489 }
5490
5491 if (wdev->ps)
5492 ps_state = NL80211_PS_ENABLED;
5493 else
5494 ps_state = NL80211_PS_DISABLED;
5495
5496 NLA_PUT_U32(msg, NL80211_ATTR_PS_STATE, ps_state);
5497
5498 genlmsg_end(msg, hdr);
Johannes Berg4c476992010-10-04 21:36:35 +02005499 return genlmsg_reply(msg, info);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005500
Johannes Berg4c476992010-10-04 21:36:35 +02005501 nla_put_failure:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005502 err = -ENOBUFS;
Johannes Berg4c476992010-10-04 21:36:35 +02005503 free_msg:
Kalle Valoffb9eb32010-02-17 17:58:10 +02005504 nlmsg_free(msg);
Kalle Valoffb9eb32010-02-17 17:58:10 +02005505 return err;
5506}
5507
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005508static struct nla_policy
5509nl80211_attr_cqm_policy[NL80211_ATTR_CQM_MAX + 1] __read_mostly = {
5510 [NL80211_ATTR_CQM_RSSI_THOLD] = { .type = NLA_U32 },
5511 [NL80211_ATTR_CQM_RSSI_HYST] = { .type = NLA_U32 },
5512 [NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT] = { .type = NLA_U32 },
5513};
5514
5515static int nl80211_set_cqm_rssi(struct genl_info *info,
5516 s32 threshold, u32 hysteresis)
5517{
Johannes Berg4c476992010-10-04 21:36:35 +02005518 struct cfg80211_registered_device *rdev = info->user_ptr[0];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005519 struct wireless_dev *wdev;
Johannes Berg4c476992010-10-04 21:36:35 +02005520 struct net_device *dev = info->user_ptr[1];
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005521
5522 if (threshold > 0)
5523 return -EINVAL;
5524
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005525 wdev = dev->ieee80211_ptr;
5526
Johannes Berg4c476992010-10-04 21:36:35 +02005527 if (!rdev->ops->set_cqm_rssi_config)
5528 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005529
Johannes Berg074ac8d2010-09-16 14:58:22 +02005530 if (wdev->iftype != NL80211_IFTYPE_STATION &&
Johannes Berg4c476992010-10-04 21:36:35 +02005531 wdev->iftype != NL80211_IFTYPE_P2P_CLIENT)
5532 return -EOPNOTSUPP;
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005533
Johannes Berg4c476992010-10-04 21:36:35 +02005534 return rdev->ops->set_cqm_rssi_config(wdev->wiphy, dev,
5535 threshold, hysteresis);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02005536}
5537
5538static int nl80211_set_cqm(struct sk_buff *skb, struct genl_info *info)
5539{
5540 struct nlattr *attrs[NL80211_ATTR_CQM_MAX + 1];
5541 struct nlattr *cqm;
5542 int err;
5543
5544 cqm = info->attrs[NL80211_ATTR_CQM];
5545 if (!cqm) {
5546 err = -EINVAL;
5547 goto out;
5548 }
5549
5550 err = nla_parse_nested(attrs, NL80211_ATTR_CQM_MAX, cqm,
5551 nl80211_attr_cqm_policy);
5552 if (err)
5553 goto out;
5554
5555 if (attrs[NL80211_ATTR_CQM_RSSI_THOLD] &&
5556 attrs[NL80211_ATTR_CQM_RSSI_HYST]) {
5557 s32 threshold;
5558 u32 hysteresis;
5559 threshold = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_THOLD]);
5560 hysteresis = nla_get_u32(attrs[NL80211_ATTR_CQM_RSSI_HYST]);
5561 err = nl80211_set_cqm_rssi(info, threshold, hysteresis);
5562 } else
5563 err = -EINVAL;
5564
5565out:
5566 return err;
5567}
5568
Johannes Berg29cbe682010-12-03 09:20:44 +01005569static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
5570{
5571 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5572 struct net_device *dev = info->user_ptr[1];
5573 struct mesh_config cfg;
Javier Cardonac80d5452010-12-16 17:37:49 -08005574 struct mesh_setup setup;
Johannes Berg29cbe682010-12-03 09:20:44 +01005575 int err;
5576
5577 /* start with default */
5578 memcpy(&cfg, &default_mesh_config, sizeof(cfg));
Javier Cardonac80d5452010-12-16 17:37:49 -08005579 memcpy(&setup, &default_mesh_setup, sizeof(setup));
Johannes Berg29cbe682010-12-03 09:20:44 +01005580
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005581 if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
Johannes Berg29cbe682010-12-03 09:20:44 +01005582 /* and parse parameters if given */
Javier Cardona24bdd9f2010-12-16 17:37:48 -08005583 err = nl80211_parse_mesh_config(info, &cfg, NULL);
Johannes Berg29cbe682010-12-03 09:20:44 +01005584 if (err)
5585 return err;
5586 }
5587
5588 if (!info->attrs[NL80211_ATTR_MESH_ID] ||
5589 !nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
5590 return -EINVAL;
5591
Javier Cardonac80d5452010-12-16 17:37:49 -08005592 setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
5593 setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
5594
5595 if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
5596 /* parse additional setup parameters if given */
5597 err = nl80211_parse_mesh_setup(info, &setup);
5598 if (err)
5599 return err;
5600 }
5601
5602 return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
Johannes Berg29cbe682010-12-03 09:20:44 +01005603}
5604
5605static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)
5606{
5607 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5608 struct net_device *dev = info->user_ptr[1];
5609
5610 return cfg80211_leave_mesh(rdev, dev);
5611}
5612
Johannes Bergff1b6e62011-05-04 15:37:28 +02005613static int nl80211_get_wowlan(struct sk_buff *skb, struct genl_info *info)
5614{
5615 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5616 struct sk_buff *msg;
5617 void *hdr;
5618
5619 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5620 return -EOPNOTSUPP;
5621
5622 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5623 if (!msg)
5624 return -ENOMEM;
5625
5626 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5627 NL80211_CMD_GET_WOWLAN);
5628 if (!hdr)
5629 goto nla_put_failure;
5630
5631 if (rdev->wowlan) {
5632 struct nlattr *nl_wowlan;
5633
5634 nl_wowlan = nla_nest_start(msg, NL80211_ATTR_WOWLAN_TRIGGERS);
5635 if (!nl_wowlan)
5636 goto nla_put_failure;
5637
5638 if (rdev->wowlan->any)
5639 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_ANY);
5640 if (rdev->wowlan->disconnect)
5641 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_DISCONNECT);
5642 if (rdev->wowlan->magic_pkt)
5643 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_MAGIC_PKT);
Johannes Berg77dbbb12011-07-13 10:48:55 +02005644 if (rdev->wowlan->gtk_rekey_failure)
5645 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE);
5646 if (rdev->wowlan->eap_identity_req)
5647 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST);
5648 if (rdev->wowlan->four_way_handshake)
5649 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE);
5650 if (rdev->wowlan->rfkill_release)
5651 NLA_PUT_FLAG(msg, NL80211_WOWLAN_TRIG_RFKILL_RELEASE);
Johannes Bergff1b6e62011-05-04 15:37:28 +02005652 if (rdev->wowlan->n_patterns) {
5653 struct nlattr *nl_pats, *nl_pat;
5654 int i, pat_len;
5655
5656 nl_pats = nla_nest_start(msg,
5657 NL80211_WOWLAN_TRIG_PKT_PATTERN);
5658 if (!nl_pats)
5659 goto nla_put_failure;
5660
5661 for (i = 0; i < rdev->wowlan->n_patterns; i++) {
5662 nl_pat = nla_nest_start(msg, i + 1);
5663 if (!nl_pat)
5664 goto nla_put_failure;
5665 pat_len = rdev->wowlan->patterns[i].pattern_len;
5666 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_MASK,
5667 DIV_ROUND_UP(pat_len, 8),
5668 rdev->wowlan->patterns[i].mask);
5669 NLA_PUT(msg, NL80211_WOWLAN_PKTPAT_PATTERN,
5670 pat_len,
5671 rdev->wowlan->patterns[i].pattern);
5672 nla_nest_end(msg, nl_pat);
5673 }
5674 nla_nest_end(msg, nl_pats);
5675 }
5676
5677 nla_nest_end(msg, nl_wowlan);
5678 }
5679
5680 genlmsg_end(msg, hdr);
5681 return genlmsg_reply(msg, info);
5682
5683nla_put_failure:
5684 nlmsg_free(msg);
5685 return -ENOBUFS;
5686}
5687
5688static int nl80211_set_wowlan(struct sk_buff *skb, struct genl_info *info)
5689{
5690 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5691 struct nlattr *tb[NUM_NL80211_WOWLAN_TRIG];
5692 struct cfg80211_wowlan no_triggers = {};
5693 struct cfg80211_wowlan new_triggers = {};
5694 struct wiphy_wowlan_support *wowlan = &rdev->wiphy.wowlan;
5695 int err, i;
5696
5697 if (!rdev->wiphy.wowlan.flags && !rdev->wiphy.wowlan.n_patterns)
5698 return -EOPNOTSUPP;
5699
5700 if (!info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS])
5701 goto no_triggers;
5702
5703 err = nla_parse(tb, MAX_NL80211_WOWLAN_TRIG,
5704 nla_data(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5705 nla_len(info->attrs[NL80211_ATTR_WOWLAN_TRIGGERS]),
5706 nl80211_wowlan_policy);
5707 if (err)
5708 return err;
5709
5710 if (tb[NL80211_WOWLAN_TRIG_ANY]) {
5711 if (!(wowlan->flags & WIPHY_WOWLAN_ANY))
5712 return -EINVAL;
5713 new_triggers.any = true;
5714 }
5715
5716 if (tb[NL80211_WOWLAN_TRIG_DISCONNECT]) {
5717 if (!(wowlan->flags & WIPHY_WOWLAN_DISCONNECT))
5718 return -EINVAL;
5719 new_triggers.disconnect = true;
5720 }
5721
5722 if (tb[NL80211_WOWLAN_TRIG_MAGIC_PKT]) {
5723 if (!(wowlan->flags & WIPHY_WOWLAN_MAGIC_PKT))
5724 return -EINVAL;
5725 new_triggers.magic_pkt = true;
5726 }
5727
Johannes Berg77dbbb12011-07-13 10:48:55 +02005728 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_SUPPORTED])
5729 return -EINVAL;
5730
5731 if (tb[NL80211_WOWLAN_TRIG_GTK_REKEY_FAILURE]) {
5732 if (!(wowlan->flags & WIPHY_WOWLAN_GTK_REKEY_FAILURE))
5733 return -EINVAL;
5734 new_triggers.gtk_rekey_failure = true;
5735 }
5736
5737 if (tb[NL80211_WOWLAN_TRIG_EAP_IDENT_REQUEST]) {
5738 if (!(wowlan->flags & WIPHY_WOWLAN_EAP_IDENTITY_REQ))
5739 return -EINVAL;
5740 new_triggers.eap_identity_req = true;
5741 }
5742
5743 if (tb[NL80211_WOWLAN_TRIG_4WAY_HANDSHAKE]) {
5744 if (!(wowlan->flags & WIPHY_WOWLAN_4WAY_HANDSHAKE))
5745 return -EINVAL;
5746 new_triggers.four_way_handshake = true;
5747 }
5748
5749 if (tb[NL80211_WOWLAN_TRIG_RFKILL_RELEASE]) {
5750 if (!(wowlan->flags & WIPHY_WOWLAN_RFKILL_RELEASE))
5751 return -EINVAL;
5752 new_triggers.rfkill_release = true;
5753 }
5754
Johannes Bergff1b6e62011-05-04 15:37:28 +02005755 if (tb[NL80211_WOWLAN_TRIG_PKT_PATTERN]) {
5756 struct nlattr *pat;
5757 int n_patterns = 0;
5758 int rem, pat_len, mask_len;
5759 struct nlattr *pat_tb[NUM_NL80211_WOWLAN_PKTPAT];
5760
5761 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5762 rem)
5763 n_patterns++;
5764 if (n_patterns > wowlan->n_patterns)
5765 return -EINVAL;
5766
5767 new_triggers.patterns = kcalloc(n_patterns,
5768 sizeof(new_triggers.patterns[0]),
5769 GFP_KERNEL);
5770 if (!new_triggers.patterns)
5771 return -ENOMEM;
5772
5773 new_triggers.n_patterns = n_patterns;
5774 i = 0;
5775
5776 nla_for_each_nested(pat, tb[NL80211_WOWLAN_TRIG_PKT_PATTERN],
5777 rem) {
5778 nla_parse(pat_tb, MAX_NL80211_WOWLAN_PKTPAT,
5779 nla_data(pat), nla_len(pat), NULL);
5780 err = -EINVAL;
5781 if (!pat_tb[NL80211_WOWLAN_PKTPAT_MASK] ||
5782 !pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN])
5783 goto error;
5784 pat_len = nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]);
5785 mask_len = DIV_ROUND_UP(pat_len, 8);
5786 if (nla_len(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]) !=
5787 mask_len)
5788 goto error;
5789 if (pat_len > wowlan->pattern_max_len ||
5790 pat_len < wowlan->pattern_min_len)
5791 goto error;
5792
5793 new_triggers.patterns[i].mask =
5794 kmalloc(mask_len + pat_len, GFP_KERNEL);
5795 if (!new_triggers.patterns[i].mask) {
5796 err = -ENOMEM;
5797 goto error;
5798 }
5799 new_triggers.patterns[i].pattern =
5800 new_triggers.patterns[i].mask + mask_len;
5801 memcpy(new_triggers.patterns[i].mask,
5802 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_MASK]),
5803 mask_len);
5804 new_triggers.patterns[i].pattern_len = pat_len;
5805 memcpy(new_triggers.patterns[i].pattern,
5806 nla_data(pat_tb[NL80211_WOWLAN_PKTPAT_PATTERN]),
5807 pat_len);
5808 i++;
5809 }
5810 }
5811
5812 if (memcmp(&new_triggers, &no_triggers, sizeof(new_triggers))) {
5813 struct cfg80211_wowlan *ntrig;
5814 ntrig = kmemdup(&new_triggers, sizeof(new_triggers),
5815 GFP_KERNEL);
5816 if (!ntrig) {
5817 err = -ENOMEM;
5818 goto error;
5819 }
5820 cfg80211_rdev_free_wowlan(rdev);
5821 rdev->wowlan = ntrig;
5822 } else {
5823 no_triggers:
5824 cfg80211_rdev_free_wowlan(rdev);
5825 rdev->wowlan = NULL;
5826 }
5827
5828 return 0;
5829 error:
5830 for (i = 0; i < new_triggers.n_patterns; i++)
5831 kfree(new_triggers.patterns[i].mask);
5832 kfree(new_triggers.patterns);
5833 return err;
5834}
5835
Johannes Berge5497d72011-07-05 16:35:40 +02005836static int nl80211_set_rekey_data(struct sk_buff *skb, struct genl_info *info)
5837{
5838 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5839 struct net_device *dev = info->user_ptr[1];
5840 struct wireless_dev *wdev = dev->ieee80211_ptr;
5841 struct nlattr *tb[NUM_NL80211_REKEY_DATA];
5842 struct cfg80211_gtk_rekey_data rekey_data;
5843 int err;
5844
5845 if (!info->attrs[NL80211_ATTR_REKEY_DATA])
5846 return -EINVAL;
5847
5848 err = nla_parse(tb, MAX_NL80211_REKEY_DATA,
5849 nla_data(info->attrs[NL80211_ATTR_REKEY_DATA]),
5850 nla_len(info->attrs[NL80211_ATTR_REKEY_DATA]),
5851 nl80211_rekey_policy);
5852 if (err)
5853 return err;
5854
5855 if (nla_len(tb[NL80211_REKEY_DATA_REPLAY_CTR]) != NL80211_REPLAY_CTR_LEN)
5856 return -ERANGE;
5857 if (nla_len(tb[NL80211_REKEY_DATA_KEK]) != NL80211_KEK_LEN)
5858 return -ERANGE;
5859 if (nla_len(tb[NL80211_REKEY_DATA_KCK]) != NL80211_KCK_LEN)
5860 return -ERANGE;
5861
5862 memcpy(rekey_data.kek, nla_data(tb[NL80211_REKEY_DATA_KEK]),
5863 NL80211_KEK_LEN);
5864 memcpy(rekey_data.kck, nla_data(tb[NL80211_REKEY_DATA_KCK]),
5865 NL80211_KCK_LEN);
5866 memcpy(rekey_data.replay_ctr,
5867 nla_data(tb[NL80211_REKEY_DATA_REPLAY_CTR]),
5868 NL80211_REPLAY_CTR_LEN);
5869
5870 wdev_lock(wdev);
5871 if (!wdev->current_bss) {
5872 err = -ENOTCONN;
5873 goto out;
5874 }
5875
5876 if (!rdev->ops->set_rekey_data) {
5877 err = -EOPNOTSUPP;
5878 goto out;
5879 }
5880
5881 err = rdev->ops->set_rekey_data(&rdev->wiphy, dev, &rekey_data);
5882 out:
5883 wdev_unlock(wdev);
5884 return err;
5885}
5886
Johannes Berg28946da2011-11-04 11:18:12 +01005887static int nl80211_register_unexpected_frame(struct sk_buff *skb,
5888 struct genl_info *info)
5889{
5890 struct net_device *dev = info->user_ptr[1];
5891 struct wireless_dev *wdev = dev->ieee80211_ptr;
5892
5893 if (wdev->iftype != NL80211_IFTYPE_AP &&
5894 wdev->iftype != NL80211_IFTYPE_P2P_GO)
5895 return -EINVAL;
5896
5897 if (wdev->ap_unexpected_nlpid)
5898 return -EBUSY;
5899
5900 wdev->ap_unexpected_nlpid = info->snd_pid;
5901 return 0;
5902}
5903
Johannes Berg7f6cf312011-11-04 11:18:15 +01005904static int nl80211_probe_client(struct sk_buff *skb,
5905 struct genl_info *info)
5906{
5907 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5908 struct net_device *dev = info->user_ptr[1];
5909 struct wireless_dev *wdev = dev->ieee80211_ptr;
5910 struct sk_buff *msg;
5911 void *hdr;
5912 const u8 *addr;
5913 u64 cookie;
5914 int err;
5915
5916 if (wdev->iftype != NL80211_IFTYPE_AP &&
5917 wdev->iftype != NL80211_IFTYPE_P2P_GO)
5918 return -EOPNOTSUPP;
5919
5920 if (!info->attrs[NL80211_ATTR_MAC])
5921 return -EINVAL;
5922
5923 if (!rdev->ops->probe_client)
5924 return -EOPNOTSUPP;
5925
5926 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
5927 if (!msg)
5928 return -ENOMEM;
5929
5930 hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0,
5931 NL80211_CMD_PROBE_CLIENT);
5932
5933 if (IS_ERR(hdr)) {
5934 err = PTR_ERR(hdr);
5935 goto free_msg;
5936 }
5937
5938 addr = nla_data(info->attrs[NL80211_ATTR_MAC]);
5939
5940 err = rdev->ops->probe_client(&rdev->wiphy, dev, addr, &cookie);
5941 if (err)
5942 goto free_msg;
5943
5944 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
5945
5946 genlmsg_end(msg, hdr);
5947
5948 return genlmsg_reply(msg, info);
5949
5950 nla_put_failure:
5951 err = -ENOBUFS;
5952 free_msg:
5953 nlmsg_free(msg);
5954 return err;
5955}
5956
Johannes Berg5e7602302011-11-04 11:18:17 +01005957static int nl80211_register_beacons(struct sk_buff *skb, struct genl_info *info)
5958{
5959 struct cfg80211_registered_device *rdev = info->user_ptr[0];
5960
5961 if (!(rdev->wiphy.flags & WIPHY_FLAG_REPORTS_OBSS))
5962 return -EOPNOTSUPP;
5963
5964 if (rdev->ap_beacons_nlpid)
5965 return -EBUSY;
5966
5967 rdev->ap_beacons_nlpid = info->snd_pid;
5968
5969 return 0;
5970}
5971
Johannes Berg4c476992010-10-04 21:36:35 +02005972#define NL80211_FLAG_NEED_WIPHY 0x01
5973#define NL80211_FLAG_NEED_NETDEV 0x02
5974#define NL80211_FLAG_NEED_RTNL 0x04
Johannes Berg41265712010-10-04 21:14:05 +02005975#define NL80211_FLAG_CHECK_NETDEV_UP 0x08
5976#define NL80211_FLAG_NEED_NETDEV_UP (NL80211_FLAG_NEED_NETDEV |\
5977 NL80211_FLAG_CHECK_NETDEV_UP)
Johannes Berg4c476992010-10-04 21:36:35 +02005978
5979static int nl80211_pre_doit(struct genl_ops *ops, struct sk_buff *skb,
5980 struct genl_info *info)
5981{
5982 struct cfg80211_registered_device *rdev;
5983 struct net_device *dev;
5984 int err;
5985 bool rtnl = ops->internal_flags & NL80211_FLAG_NEED_RTNL;
5986
5987 if (rtnl)
5988 rtnl_lock();
5989
5990 if (ops->internal_flags & NL80211_FLAG_NEED_WIPHY) {
5991 rdev = cfg80211_get_dev_from_info(info);
5992 if (IS_ERR(rdev)) {
5993 if (rtnl)
5994 rtnl_unlock();
5995 return PTR_ERR(rdev);
5996 }
5997 info->user_ptr[0] = rdev;
5998 } else if (ops->internal_flags & NL80211_FLAG_NEED_NETDEV) {
5999 err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
6000 if (err) {
6001 if (rtnl)
6002 rtnl_unlock();
6003 return err;
6004 }
Johannes Berg41265712010-10-04 21:14:05 +02006005 if (ops->internal_flags & NL80211_FLAG_CHECK_NETDEV_UP &&
6006 !netif_running(dev)) {
Johannes Bergd537f5f2010-10-05 21:34:11 +02006007 cfg80211_unlock_rdev(rdev);
6008 dev_put(dev);
Johannes Berg41265712010-10-04 21:14:05 +02006009 if (rtnl)
6010 rtnl_unlock();
6011 return -ENETDOWN;
6012 }
Johannes Berg4c476992010-10-04 21:36:35 +02006013 info->user_ptr[0] = rdev;
6014 info->user_ptr[1] = dev;
6015 }
6016
6017 return 0;
6018}
6019
6020static void nl80211_post_doit(struct genl_ops *ops, struct sk_buff *skb,
6021 struct genl_info *info)
6022{
6023 if (info->user_ptr[0])
6024 cfg80211_unlock_rdev(info->user_ptr[0]);
6025 if (info->user_ptr[1])
6026 dev_put(info->user_ptr[1]);
6027 if (ops->internal_flags & NL80211_FLAG_NEED_RTNL)
6028 rtnl_unlock();
6029}
6030
Johannes Berg55682962007-09-20 13:09:35 -04006031static struct genl_ops nl80211_ops[] = {
6032 {
6033 .cmd = NL80211_CMD_GET_WIPHY,
6034 .doit = nl80211_get_wiphy,
6035 .dumpit = nl80211_dump_wiphy,
6036 .policy = nl80211_policy,
6037 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006038 .internal_flags = NL80211_FLAG_NEED_WIPHY,
Johannes Berg55682962007-09-20 13:09:35 -04006039 },
6040 {
6041 .cmd = NL80211_CMD_SET_WIPHY,
6042 .doit = nl80211_set_wiphy,
6043 .policy = nl80211_policy,
6044 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006045 .internal_flags = NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006046 },
6047 {
6048 .cmd = NL80211_CMD_GET_INTERFACE,
6049 .doit = nl80211_get_interface,
6050 .dumpit = nl80211_dump_interface,
6051 .policy = nl80211_policy,
6052 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006053 .internal_flags = NL80211_FLAG_NEED_NETDEV,
Johannes Berg55682962007-09-20 13:09:35 -04006054 },
6055 {
6056 .cmd = NL80211_CMD_SET_INTERFACE,
6057 .doit = nl80211_set_interface,
6058 .policy = nl80211_policy,
6059 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006060 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6061 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006062 },
6063 {
6064 .cmd = NL80211_CMD_NEW_INTERFACE,
6065 .doit = nl80211_new_interface,
6066 .policy = nl80211_policy,
6067 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006068 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6069 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006070 },
6071 {
6072 .cmd = NL80211_CMD_DEL_INTERFACE,
6073 .doit = nl80211_del_interface,
6074 .policy = nl80211_policy,
6075 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006076 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6077 NL80211_FLAG_NEED_RTNL,
Johannes Berg55682962007-09-20 13:09:35 -04006078 },
Johannes Berg41ade002007-12-19 02:03:29 +01006079 {
6080 .cmd = NL80211_CMD_GET_KEY,
6081 .doit = nl80211_get_key,
6082 .policy = nl80211_policy,
6083 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006084 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6085 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006086 },
6087 {
6088 .cmd = NL80211_CMD_SET_KEY,
6089 .doit = nl80211_set_key,
6090 .policy = nl80211_policy,
6091 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006092 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006093 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006094 },
6095 {
6096 .cmd = NL80211_CMD_NEW_KEY,
6097 .doit = nl80211_new_key,
6098 .policy = nl80211_policy,
6099 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006100 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006101 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006102 },
6103 {
6104 .cmd = NL80211_CMD_DEL_KEY,
6105 .doit = nl80211_del_key,
6106 .policy = nl80211_policy,
6107 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006108 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006109 NL80211_FLAG_NEED_RTNL,
Johannes Berg41ade002007-12-19 02:03:29 +01006110 },
Johannes Berged1b6cc2007-12-19 02:03:32 +01006111 {
6112 .cmd = NL80211_CMD_SET_BEACON,
6113 .policy = nl80211_policy,
6114 .flags = GENL_ADMIN_PERM,
6115 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02006116 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6117 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006118 },
6119 {
6120 .cmd = NL80211_CMD_NEW_BEACON,
6121 .policy = nl80211_policy,
6122 .flags = GENL_ADMIN_PERM,
6123 .doit = nl80211_addset_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02006124 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6125 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006126 },
6127 {
6128 .cmd = NL80211_CMD_DEL_BEACON,
6129 .policy = nl80211_policy,
6130 .flags = GENL_ADMIN_PERM,
6131 .doit = nl80211_del_beacon,
Johannes Berg4c476992010-10-04 21:36:35 +02006132 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6133 NL80211_FLAG_NEED_RTNL,
Johannes Berged1b6cc2007-12-19 02:03:32 +01006134 },
Johannes Berg5727ef12007-12-19 02:03:34 +01006135 {
6136 .cmd = NL80211_CMD_GET_STATION,
6137 .doit = nl80211_get_station,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006138 .dumpit = nl80211_dump_station,
Johannes Berg5727ef12007-12-19 02:03:34 +01006139 .policy = nl80211_policy,
Johannes Berg4c476992010-10-04 21:36:35 +02006140 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6141 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006142 },
6143 {
6144 .cmd = NL80211_CMD_SET_STATION,
6145 .doit = nl80211_set_station,
6146 .policy = nl80211_policy,
6147 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006148 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6149 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006150 },
6151 {
6152 .cmd = NL80211_CMD_NEW_STATION,
6153 .doit = nl80211_new_station,
6154 .policy = nl80211_policy,
6155 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006156 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006157 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006158 },
6159 {
6160 .cmd = NL80211_CMD_DEL_STATION,
6161 .doit = nl80211_del_station,
6162 .policy = nl80211_policy,
6163 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006164 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6165 NL80211_FLAG_NEED_RTNL,
Johannes Berg5727ef12007-12-19 02:03:34 +01006166 },
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006167 {
6168 .cmd = NL80211_CMD_GET_MPATH,
6169 .doit = nl80211_get_mpath,
6170 .dumpit = nl80211_dump_mpath,
6171 .policy = nl80211_policy,
6172 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006173 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006174 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006175 },
6176 {
6177 .cmd = NL80211_CMD_SET_MPATH,
6178 .doit = nl80211_set_mpath,
6179 .policy = nl80211_policy,
6180 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006181 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006182 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006183 },
6184 {
6185 .cmd = NL80211_CMD_NEW_MPATH,
6186 .doit = nl80211_new_mpath,
6187 .policy = nl80211_policy,
6188 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006189 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006190 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006191 },
6192 {
6193 .cmd = NL80211_CMD_DEL_MPATH,
6194 .doit = nl80211_del_mpath,
6195 .policy = nl80211_policy,
6196 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006197 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6198 NL80211_FLAG_NEED_RTNL,
Luis Carlos Cobo2ec600d2008-02-23 15:17:06 +01006199 },
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006200 {
6201 .cmd = NL80211_CMD_SET_BSS,
6202 .doit = nl80211_set_bss,
6203 .policy = nl80211_policy,
6204 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006205 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6206 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9f1ba902008-08-07 20:07:01 +03006207 },
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006208 {
Luis R. Rodriguezf1303472009-01-30 09:26:42 -08006209 .cmd = NL80211_CMD_GET_REG,
6210 .doit = nl80211_get_reg,
6211 .policy = nl80211_policy,
6212 /* can be retrieved by unprivileged users */
6213 },
6214 {
Luis R. Rodriguezb2e1b302008-09-09 23:19:48 -07006215 .cmd = NL80211_CMD_SET_REG,
6216 .doit = nl80211_set_reg,
6217 .policy = nl80211_policy,
6218 .flags = GENL_ADMIN_PERM,
6219 },
6220 {
6221 .cmd = NL80211_CMD_REQ_SET_REG,
6222 .doit = nl80211_req_set_reg,
6223 .policy = nl80211_policy,
6224 .flags = GENL_ADMIN_PERM,
6225 },
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006226 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006227 .cmd = NL80211_CMD_GET_MESH_CONFIG,
6228 .doit = nl80211_get_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006229 .policy = nl80211_policy,
6230 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006231 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6232 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006233 },
6234 {
Javier Cardona24bdd9f2010-12-16 17:37:48 -08006235 .cmd = NL80211_CMD_SET_MESH_CONFIG,
6236 .doit = nl80211_update_mesh_config,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006237 .policy = nl80211_policy,
6238 .flags = GENL_ADMIN_PERM,
Johannes Berg29cbe682010-12-03 09:20:44 +01006239 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006240 NL80211_FLAG_NEED_RTNL,
colin@cozybit.com93da9cc2008-10-21 12:03:48 -07006241 },
Jouni Malinen9aed3cc2009-01-13 16:03:29 +02006242 {
Johannes Berg2a519312009-02-10 21:25:55 +01006243 .cmd = NL80211_CMD_TRIGGER_SCAN,
6244 .doit = nl80211_trigger_scan,
6245 .policy = nl80211_policy,
6246 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006247 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006248 NL80211_FLAG_NEED_RTNL,
Johannes Berg2a519312009-02-10 21:25:55 +01006249 },
6250 {
6251 .cmd = NL80211_CMD_GET_SCAN,
6252 .policy = nl80211_policy,
6253 .dumpit = nl80211_dump_scan,
6254 },
Jouni Malinen636a5d32009-03-19 13:39:22 +02006255 {
Luciano Coelho807f8a82011-05-11 17:09:35 +03006256 .cmd = NL80211_CMD_START_SCHED_SCAN,
6257 .doit = nl80211_start_sched_scan,
6258 .policy = nl80211_policy,
6259 .flags = GENL_ADMIN_PERM,
6260 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6261 NL80211_FLAG_NEED_RTNL,
6262 },
6263 {
6264 .cmd = NL80211_CMD_STOP_SCHED_SCAN,
6265 .doit = nl80211_stop_sched_scan,
6266 .policy = nl80211_policy,
6267 .flags = GENL_ADMIN_PERM,
6268 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6269 NL80211_FLAG_NEED_RTNL,
6270 },
6271 {
Jouni Malinen636a5d32009-03-19 13:39:22 +02006272 .cmd = NL80211_CMD_AUTHENTICATE,
6273 .doit = nl80211_authenticate,
6274 .policy = nl80211_policy,
6275 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006276 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006277 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006278 },
6279 {
6280 .cmd = NL80211_CMD_ASSOCIATE,
6281 .doit = nl80211_associate,
6282 .policy = nl80211_policy,
6283 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006284 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006285 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006286 },
6287 {
6288 .cmd = NL80211_CMD_DEAUTHENTICATE,
6289 .doit = nl80211_deauthenticate,
6290 .policy = nl80211_policy,
6291 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006292 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006293 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006294 },
6295 {
6296 .cmd = NL80211_CMD_DISASSOCIATE,
6297 .doit = nl80211_disassociate,
6298 .policy = nl80211_policy,
6299 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006300 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006301 NL80211_FLAG_NEED_RTNL,
Jouni Malinen636a5d32009-03-19 13:39:22 +02006302 },
Johannes Berg04a773a2009-04-19 21:24:32 +02006303 {
6304 .cmd = NL80211_CMD_JOIN_IBSS,
6305 .doit = nl80211_join_ibss,
6306 .policy = nl80211_policy,
6307 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006308 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006309 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006310 },
6311 {
6312 .cmd = NL80211_CMD_LEAVE_IBSS,
6313 .doit = nl80211_leave_ibss,
6314 .policy = nl80211_policy,
6315 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006316 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006317 NL80211_FLAG_NEED_RTNL,
Johannes Berg04a773a2009-04-19 21:24:32 +02006318 },
Johannes Bergaff89a92009-07-01 21:26:51 +02006319#ifdef CONFIG_NL80211_TESTMODE
6320 {
6321 .cmd = NL80211_CMD_TESTMODE,
6322 .doit = nl80211_testmode_do,
Wey-Yi Guy71063f02011-05-20 09:05:54 -07006323 .dumpit = nl80211_testmode_dump,
Johannes Bergaff89a92009-07-01 21:26:51 +02006324 .policy = nl80211_policy,
6325 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006326 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6327 NL80211_FLAG_NEED_RTNL,
Johannes Bergaff89a92009-07-01 21:26:51 +02006328 },
6329#endif
Samuel Ortizb23aa672009-07-01 21:26:54 +02006330 {
6331 .cmd = NL80211_CMD_CONNECT,
6332 .doit = nl80211_connect,
6333 .policy = nl80211_policy,
6334 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006335 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006336 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006337 },
6338 {
6339 .cmd = NL80211_CMD_DISCONNECT,
6340 .doit = nl80211_disconnect,
6341 .policy = nl80211_policy,
6342 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006343 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006344 NL80211_FLAG_NEED_RTNL,
Samuel Ortizb23aa672009-07-01 21:26:54 +02006345 },
Johannes Berg463d0182009-07-14 00:33:35 +02006346 {
6347 .cmd = NL80211_CMD_SET_WIPHY_NETNS,
6348 .doit = nl80211_wiphy_netns,
6349 .policy = nl80211_policy,
6350 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006351 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6352 NL80211_FLAG_NEED_RTNL,
Johannes Berg463d0182009-07-14 00:33:35 +02006353 },
Holger Schurig61fa7132009-11-11 12:25:40 +01006354 {
6355 .cmd = NL80211_CMD_GET_SURVEY,
6356 .policy = nl80211_policy,
6357 .dumpit = nl80211_dump_survey,
6358 },
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006359 {
6360 .cmd = NL80211_CMD_SET_PMKSA,
6361 .doit = nl80211_setdel_pmksa,
6362 .policy = nl80211_policy,
6363 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006364 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6365 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006366 },
6367 {
6368 .cmd = NL80211_CMD_DEL_PMKSA,
6369 .doit = nl80211_setdel_pmksa,
6370 .policy = nl80211_policy,
6371 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006372 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6373 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006374 },
6375 {
6376 .cmd = NL80211_CMD_FLUSH_PMKSA,
6377 .doit = nl80211_flush_pmksa,
6378 .policy = nl80211_policy,
6379 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006380 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6381 NL80211_FLAG_NEED_RTNL,
Samuel Ortiz67fbb162009-11-24 23:59:15 +01006382 },
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006383 {
6384 .cmd = NL80211_CMD_REMAIN_ON_CHANNEL,
6385 .doit = nl80211_remain_on_channel,
6386 .policy = nl80211_policy,
6387 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006388 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006389 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006390 },
6391 {
6392 .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
6393 .doit = nl80211_cancel_remain_on_channel,
6394 .policy = nl80211_policy,
6395 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006396 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006397 NL80211_FLAG_NEED_RTNL,
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006398 },
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006399 {
6400 .cmd = NL80211_CMD_SET_TX_BITRATE_MASK,
6401 .doit = nl80211_set_tx_bitrate_mask,
6402 .policy = nl80211_policy,
6403 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006404 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6405 NL80211_FLAG_NEED_RTNL,
Jouni Malinen13ae75b2009-12-29 12:59:45 +02006406 },
Jouni Malinen026331c2010-02-15 12:53:10 +02006407 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006408 .cmd = NL80211_CMD_REGISTER_FRAME,
6409 .doit = nl80211_register_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006410 .policy = nl80211_policy,
6411 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006412 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6413 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006414 },
6415 {
Johannes Berg2e161f72010-08-12 15:38:38 +02006416 .cmd = NL80211_CMD_FRAME,
6417 .doit = nl80211_tx_mgmt,
Jouni Malinen026331c2010-02-15 12:53:10 +02006418 .policy = nl80211_policy,
6419 .flags = GENL_ADMIN_PERM,
Johannes Berg41265712010-10-04 21:14:05 +02006420 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
Johannes Berg4c476992010-10-04 21:36:35 +02006421 NL80211_FLAG_NEED_RTNL,
Jouni Malinen026331c2010-02-15 12:53:10 +02006422 },
Kalle Valoffb9eb32010-02-17 17:58:10 +02006423 {
Johannes Bergf7ca38d2010-11-25 10:02:29 +01006424 .cmd = NL80211_CMD_FRAME_WAIT_CANCEL,
6425 .doit = nl80211_tx_mgmt_cancel_wait,
6426 .policy = nl80211_policy,
6427 .flags = GENL_ADMIN_PERM,
6428 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6429 NL80211_FLAG_NEED_RTNL,
6430 },
6431 {
Kalle Valoffb9eb32010-02-17 17:58:10 +02006432 .cmd = NL80211_CMD_SET_POWER_SAVE,
6433 .doit = nl80211_set_power_save,
6434 .policy = nl80211_policy,
6435 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006436 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6437 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006438 },
6439 {
6440 .cmd = NL80211_CMD_GET_POWER_SAVE,
6441 .doit = nl80211_get_power_save,
6442 .policy = nl80211_policy,
6443 /* can be retrieved by unprivileged users */
Johannes Berg4c476992010-10-04 21:36:35 +02006444 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6445 NL80211_FLAG_NEED_RTNL,
Kalle Valoffb9eb32010-02-17 17:58:10 +02006446 },
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006447 {
6448 .cmd = NL80211_CMD_SET_CQM,
6449 .doit = nl80211_set_cqm,
6450 .policy = nl80211_policy,
6451 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006452 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6453 NL80211_FLAG_NEED_RTNL,
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02006454 },
Johannes Bergf444de02010-05-05 15:25:02 +02006455 {
6456 .cmd = NL80211_CMD_SET_CHANNEL,
6457 .doit = nl80211_set_channel,
6458 .policy = nl80211_policy,
6459 .flags = GENL_ADMIN_PERM,
Johannes Berg4c476992010-10-04 21:36:35 +02006460 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6461 NL80211_FLAG_NEED_RTNL,
Johannes Bergf444de02010-05-05 15:25:02 +02006462 },
Bill Jordane8347eb2010-10-01 13:54:28 -04006463 {
6464 .cmd = NL80211_CMD_SET_WDS_PEER,
6465 .doit = nl80211_set_wds_peer,
6466 .policy = nl80211_policy,
6467 .flags = GENL_ADMIN_PERM,
Johannes Berg43b19952010-10-07 13:10:30 +02006468 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6469 NL80211_FLAG_NEED_RTNL,
Bill Jordane8347eb2010-10-01 13:54:28 -04006470 },
Johannes Berg29cbe682010-12-03 09:20:44 +01006471 {
6472 .cmd = NL80211_CMD_JOIN_MESH,
6473 .doit = nl80211_join_mesh,
6474 .policy = nl80211_policy,
6475 .flags = GENL_ADMIN_PERM,
6476 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6477 NL80211_FLAG_NEED_RTNL,
6478 },
6479 {
6480 .cmd = NL80211_CMD_LEAVE_MESH,
6481 .doit = nl80211_leave_mesh,
6482 .policy = nl80211_policy,
6483 .flags = GENL_ADMIN_PERM,
6484 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6485 NL80211_FLAG_NEED_RTNL,
6486 },
Johannes Bergff1b6e62011-05-04 15:37:28 +02006487 {
6488 .cmd = NL80211_CMD_GET_WOWLAN,
6489 .doit = nl80211_get_wowlan,
6490 .policy = nl80211_policy,
6491 /* can be retrieved by unprivileged users */
6492 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6493 NL80211_FLAG_NEED_RTNL,
6494 },
6495 {
6496 .cmd = NL80211_CMD_SET_WOWLAN,
6497 .doit = nl80211_set_wowlan,
6498 .policy = nl80211_policy,
6499 .flags = GENL_ADMIN_PERM,
6500 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6501 NL80211_FLAG_NEED_RTNL,
6502 },
Johannes Berge5497d72011-07-05 16:35:40 +02006503 {
6504 .cmd = NL80211_CMD_SET_REKEY_OFFLOAD,
6505 .doit = nl80211_set_rekey_data,
6506 .policy = nl80211_policy,
6507 .flags = GENL_ADMIN_PERM,
6508 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6509 NL80211_FLAG_NEED_RTNL,
6510 },
Arik Nemtsov109086c2011-09-28 14:12:50 +03006511 {
6512 .cmd = NL80211_CMD_TDLS_MGMT,
6513 .doit = nl80211_tdls_mgmt,
6514 .policy = nl80211_policy,
6515 .flags = GENL_ADMIN_PERM,
6516 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6517 NL80211_FLAG_NEED_RTNL,
6518 },
6519 {
6520 .cmd = NL80211_CMD_TDLS_OPER,
6521 .doit = nl80211_tdls_oper,
6522 .policy = nl80211_policy,
6523 .flags = GENL_ADMIN_PERM,
6524 .internal_flags = NL80211_FLAG_NEED_NETDEV_UP |
6525 NL80211_FLAG_NEED_RTNL,
6526 },
Johannes Berg28946da2011-11-04 11:18:12 +01006527 {
6528 .cmd = NL80211_CMD_UNEXPECTED_FRAME,
6529 .doit = nl80211_register_unexpected_frame,
6530 .policy = nl80211_policy,
6531 .flags = GENL_ADMIN_PERM,
6532 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6533 NL80211_FLAG_NEED_RTNL,
6534 },
Johannes Berg7f6cf312011-11-04 11:18:15 +01006535 {
6536 .cmd = NL80211_CMD_PROBE_CLIENT,
6537 .doit = nl80211_probe_client,
6538 .policy = nl80211_policy,
6539 .flags = GENL_ADMIN_PERM,
6540 .internal_flags = NL80211_FLAG_NEED_NETDEV |
6541 NL80211_FLAG_NEED_RTNL,
6542 },
Johannes Berg5e7602302011-11-04 11:18:17 +01006543 {
6544 .cmd = NL80211_CMD_REGISTER_BEACONS,
6545 .doit = nl80211_register_beacons,
6546 .policy = nl80211_policy,
6547 .flags = GENL_ADMIN_PERM,
6548 .internal_flags = NL80211_FLAG_NEED_WIPHY |
6549 NL80211_FLAG_NEED_RTNL,
6550 },
Johannes Berg55682962007-09-20 13:09:35 -04006551};
Jouni Malinen9588bbd2009-12-23 13:15:41 +01006552
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006553static struct genl_multicast_group nl80211_mlme_mcgrp = {
6554 .name = "mlme",
6555};
Johannes Berg55682962007-09-20 13:09:35 -04006556
6557/* multicast groups */
6558static struct genl_multicast_group nl80211_config_mcgrp = {
6559 .name = "config",
6560};
Johannes Berg2a519312009-02-10 21:25:55 +01006561static struct genl_multicast_group nl80211_scan_mcgrp = {
6562 .name = "scan",
6563};
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006564static struct genl_multicast_group nl80211_regulatory_mcgrp = {
6565 .name = "regulatory",
6566};
Johannes Berg55682962007-09-20 13:09:35 -04006567
6568/* notification functions */
6569
6570void nl80211_notify_dev_rename(struct cfg80211_registered_device *rdev)
6571{
6572 struct sk_buff *msg;
6573
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006574 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006575 if (!msg)
6576 return;
6577
6578 if (nl80211_send_wiphy(msg, 0, 0, 0, rdev) < 0) {
6579 nlmsg_free(msg);
6580 return;
6581 }
6582
Johannes Berg463d0182009-07-14 00:33:35 +02006583 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6584 nl80211_config_mcgrp.id, GFP_KERNEL);
Johannes Berg55682962007-09-20 13:09:35 -04006585}
6586
Johannes Berg362a4152009-05-24 16:43:15 +02006587static int nl80211_add_scan_req(struct sk_buff *msg,
6588 struct cfg80211_registered_device *rdev)
6589{
6590 struct cfg80211_scan_request *req = rdev->scan_req;
6591 struct nlattr *nest;
6592 int i;
6593
Johannes Berg667503dd2009-07-07 03:56:11 +02006594 ASSERT_RDEV_LOCK(rdev);
6595
Johannes Berg362a4152009-05-24 16:43:15 +02006596 if (WARN_ON(!req))
6597 return 0;
6598
6599 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_SSIDS);
6600 if (!nest)
6601 goto nla_put_failure;
6602 for (i = 0; i < req->n_ssids; i++)
6603 NLA_PUT(msg, i, req->ssids[i].ssid_len, req->ssids[i].ssid);
6604 nla_nest_end(msg, nest);
6605
6606 nest = nla_nest_start(msg, NL80211_ATTR_SCAN_FREQUENCIES);
6607 if (!nest)
6608 goto nla_put_failure;
6609 for (i = 0; i < req->n_channels; i++)
6610 NLA_PUT_U32(msg, i, req->channels[i]->center_freq);
6611 nla_nest_end(msg, nest);
6612
6613 if (req->ie)
6614 NLA_PUT(msg, NL80211_ATTR_IE, req->ie_len, req->ie);
6615
6616 return 0;
6617 nla_put_failure:
6618 return -ENOBUFS;
6619}
6620
Johannes Berga538e2d2009-06-16 19:56:42 +02006621static int nl80211_send_scan_msg(struct sk_buff *msg,
6622 struct cfg80211_registered_device *rdev,
6623 struct net_device *netdev,
6624 u32 pid, u32 seq, int flags,
6625 u32 cmd)
Johannes Berg2a519312009-02-10 21:25:55 +01006626{
6627 void *hdr;
6628
6629 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6630 if (!hdr)
6631 return -1;
6632
Luis R. Rodriguezb5850a72009-02-21 00:04:19 -05006633 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
Johannes Berg2a519312009-02-10 21:25:55 +01006634 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6635
Johannes Berg362a4152009-05-24 16:43:15 +02006636 /* ignore errors and send incomplete event anyway */
6637 nl80211_add_scan_req(msg, rdev);
Johannes Berg2a519312009-02-10 21:25:55 +01006638
6639 return genlmsg_end(msg, hdr);
6640
6641 nla_put_failure:
6642 genlmsg_cancel(msg, hdr);
6643 return -EMSGSIZE;
6644}
6645
Luciano Coelho807f8a82011-05-11 17:09:35 +03006646static int
6647nl80211_send_sched_scan_msg(struct sk_buff *msg,
6648 struct cfg80211_registered_device *rdev,
6649 struct net_device *netdev,
6650 u32 pid, u32 seq, int flags, u32 cmd)
6651{
6652 void *hdr;
6653
6654 hdr = nl80211hdr_put(msg, pid, seq, flags, cmd);
6655 if (!hdr)
6656 return -1;
6657
6658 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6659 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6660
6661 return genlmsg_end(msg, hdr);
6662
6663 nla_put_failure:
6664 genlmsg_cancel(msg, hdr);
6665 return -EMSGSIZE;
6666}
6667
Johannes Berga538e2d2009-06-16 19:56:42 +02006668void nl80211_send_scan_start(struct cfg80211_registered_device *rdev,
6669 struct net_device *netdev)
6670{
6671 struct sk_buff *msg;
6672
6673 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6674 if (!msg)
6675 return;
6676
6677 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6678 NL80211_CMD_TRIGGER_SCAN) < 0) {
6679 nlmsg_free(msg);
6680 return;
6681 }
6682
Johannes Berg463d0182009-07-14 00:33:35 +02006683 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6684 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berga538e2d2009-06-16 19:56:42 +02006685}
6686
Johannes Berg2a519312009-02-10 21:25:55 +01006687void nl80211_send_scan_done(struct cfg80211_registered_device *rdev,
6688 struct net_device *netdev)
6689{
6690 struct sk_buff *msg;
6691
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006692 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006693 if (!msg)
6694 return;
6695
Johannes Berga538e2d2009-06-16 19:56:42 +02006696 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6697 NL80211_CMD_NEW_SCAN_RESULTS) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006698 nlmsg_free(msg);
6699 return;
6700 }
6701
Johannes Berg463d0182009-07-14 00:33:35 +02006702 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6703 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006704}
6705
6706void nl80211_send_scan_aborted(struct cfg80211_registered_device *rdev,
6707 struct net_device *netdev)
6708{
6709 struct sk_buff *msg;
6710
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006711 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006712 if (!msg)
6713 return;
6714
Johannes Berga538e2d2009-06-16 19:56:42 +02006715 if (nl80211_send_scan_msg(msg, rdev, netdev, 0, 0, 0,
6716 NL80211_CMD_SCAN_ABORTED) < 0) {
Johannes Berg2a519312009-02-10 21:25:55 +01006717 nlmsg_free(msg);
6718 return;
6719 }
6720
Johannes Berg463d0182009-07-14 00:33:35 +02006721 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6722 nl80211_scan_mcgrp.id, GFP_KERNEL);
Johannes Berg2a519312009-02-10 21:25:55 +01006723}
6724
Luciano Coelho807f8a82011-05-11 17:09:35 +03006725void nl80211_send_sched_scan_results(struct cfg80211_registered_device *rdev,
6726 struct net_device *netdev)
6727{
6728 struct sk_buff *msg;
6729
6730 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
6731 if (!msg)
6732 return;
6733
6734 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0,
6735 NL80211_CMD_SCHED_SCAN_RESULTS) < 0) {
6736 nlmsg_free(msg);
6737 return;
6738 }
6739
6740 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6741 nl80211_scan_mcgrp.id, GFP_KERNEL);
6742}
6743
6744void nl80211_send_sched_scan(struct cfg80211_registered_device *rdev,
6745 struct net_device *netdev, u32 cmd)
6746{
6747 struct sk_buff *msg;
6748
6749 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
6750 if (!msg)
6751 return;
6752
6753 if (nl80211_send_sched_scan_msg(msg, rdev, netdev, 0, 0, 0, cmd) < 0) {
6754 nlmsg_free(msg);
6755 return;
6756 }
6757
6758 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6759 nl80211_scan_mcgrp.id, GFP_KERNEL);
6760}
6761
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006762/*
6763 * This can happen on global regulatory changes or device specific settings
6764 * based on custom world regulatory domains.
6765 */
6766void nl80211_send_reg_change_event(struct regulatory_request *request)
6767{
6768 struct sk_buff *msg;
6769 void *hdr;
6770
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07006771 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006772 if (!msg)
6773 return;
6774
6775 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_CHANGE);
6776 if (!hdr) {
6777 nlmsg_free(msg);
6778 return;
6779 }
6780
6781 /* Userspace can always count this one always being set */
6782 NLA_PUT_U8(msg, NL80211_ATTR_REG_INITIATOR, request->initiator);
6783
6784 if (request->alpha2[0] == '0' && request->alpha2[1] == '0')
6785 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6786 NL80211_REGDOM_TYPE_WORLD);
6787 else if (request->alpha2[0] == '9' && request->alpha2[1] == '9')
6788 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6789 NL80211_REGDOM_TYPE_CUSTOM_WORLD);
6790 else if ((request->alpha2[0] == '9' && request->alpha2[1] == '8') ||
6791 request->intersect)
6792 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6793 NL80211_REGDOM_TYPE_INTERSECTION);
6794 else {
6795 NLA_PUT_U8(msg, NL80211_ATTR_REG_TYPE,
6796 NL80211_REGDOM_TYPE_COUNTRY);
6797 NLA_PUT_STRING(msg, NL80211_ATTR_REG_ALPHA2, request->alpha2);
6798 }
6799
6800 if (wiphy_idx_valid(request->wiphy_idx))
6801 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, request->wiphy_idx);
6802
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006803 genlmsg_end(msg, hdr);
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006804
Johannes Bergbc43b282009-07-25 10:54:13 +02006805 rcu_read_lock();
Johannes Berg463d0182009-07-14 00:33:35 +02006806 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
Johannes Bergbc43b282009-07-25 10:54:13 +02006807 GFP_ATOMIC);
6808 rcu_read_unlock();
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04006809
6810 return;
6811
6812nla_put_failure:
6813 genlmsg_cancel(msg, hdr);
6814 nlmsg_free(msg);
6815}
6816
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006817static void nl80211_send_mlme_event(struct cfg80211_registered_device *rdev,
6818 struct net_device *netdev,
6819 const u8 *buf, size_t len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006820 enum nl80211_commands cmd, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006821{
6822 struct sk_buff *msg;
6823 void *hdr;
6824
Johannes Berge6d6e342009-07-01 21:26:47 +02006825 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006826 if (!msg)
6827 return;
6828
6829 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6830 if (!hdr) {
6831 nlmsg_free(msg);
6832 return;
6833 }
6834
6835 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6836 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6837 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
6838
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006839 genlmsg_end(msg, hdr);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006840
Johannes Berg463d0182009-07-14 00:33:35 +02006841 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6842 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006843 return;
6844
6845 nla_put_failure:
6846 genlmsg_cancel(msg, hdr);
6847 nlmsg_free(msg);
6848}
6849
6850void nl80211_send_rx_auth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006851 struct net_device *netdev, const u8 *buf,
6852 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006853{
6854 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006855 NL80211_CMD_AUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006856}
6857
6858void nl80211_send_rx_assoc(struct cfg80211_registered_device *rdev,
6859 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006860 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006861{
Johannes Berge6d6e342009-07-01 21:26:47 +02006862 nl80211_send_mlme_event(rdev, netdev, buf, len,
6863 NL80211_CMD_ASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006864}
6865
Jouni Malinen53b46b82009-03-27 20:53:56 +02006866void nl80211_send_deauth(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006867 struct net_device *netdev, const u8 *buf,
6868 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006869{
6870 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006871 NL80211_CMD_DEAUTHENTICATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006872}
6873
Jouni Malinen53b46b82009-03-27 20:53:56 +02006874void nl80211_send_disassoc(struct cfg80211_registered_device *rdev,
6875 struct net_device *netdev, const u8 *buf,
Johannes Berge6d6e342009-07-01 21:26:47 +02006876 size_t len, gfp_t gfp)
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006877{
6878 nl80211_send_mlme_event(rdev, netdev, buf, len,
Johannes Berge6d6e342009-07-01 21:26:47 +02006879 NL80211_CMD_DISASSOCIATE, gfp);
Jouni Malinen6039f6d2009-03-19 13:39:21 +02006880}
6881
Jouni Malinencf4e5942010-12-16 00:52:40 +02006882void nl80211_send_unprot_deauth(struct cfg80211_registered_device *rdev,
6883 struct net_device *netdev, const u8 *buf,
6884 size_t len, gfp_t gfp)
6885{
6886 nl80211_send_mlme_event(rdev, netdev, buf, len,
6887 NL80211_CMD_UNPROT_DEAUTHENTICATE, gfp);
6888}
6889
6890void nl80211_send_unprot_disassoc(struct cfg80211_registered_device *rdev,
6891 struct net_device *netdev, const u8 *buf,
6892 size_t len, gfp_t gfp)
6893{
6894 nl80211_send_mlme_event(rdev, netdev, buf, len,
6895 NL80211_CMD_UNPROT_DISASSOCIATE, gfp);
6896}
6897
Luis R. Rodriguez1b06bb42009-05-02 00:34:48 -04006898static void nl80211_send_mlme_timeout(struct cfg80211_registered_device *rdev,
6899 struct net_device *netdev, int cmd,
Johannes Berge6d6e342009-07-01 21:26:47 +02006900 const u8 *addr, gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006901{
6902 struct sk_buff *msg;
6903 void *hdr;
6904
Johannes Berge6d6e342009-07-01 21:26:47 +02006905 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006906 if (!msg)
6907 return;
6908
6909 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
6910 if (!hdr) {
6911 nlmsg_free(msg);
6912 return;
6913 }
6914
6915 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6916 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6917 NLA_PUT_FLAG(msg, NL80211_ATTR_TIMED_OUT);
6918 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
6919
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006920 genlmsg_end(msg, hdr);
Jouni Malinen1965c852009-04-22 21:38:25 +03006921
Johannes Berg463d0182009-07-14 00:33:35 +02006922 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6923 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006924 return;
6925
6926 nla_put_failure:
6927 genlmsg_cancel(msg, hdr);
6928 nlmsg_free(msg);
6929}
6930
6931void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006932 struct net_device *netdev, const u8 *addr,
6933 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006934{
6935 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_AUTHENTICATE,
Johannes Berge6d6e342009-07-01 21:26:47 +02006936 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006937}
6938
6939void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev,
Johannes Berge6d6e342009-07-01 21:26:47 +02006940 struct net_device *netdev, const u8 *addr,
6941 gfp_t gfp)
Jouni Malinen1965c852009-04-22 21:38:25 +03006942{
Johannes Berge6d6e342009-07-01 21:26:47 +02006943 nl80211_send_mlme_timeout(rdev, netdev, NL80211_CMD_ASSOCIATE,
6944 addr, gfp);
Jouni Malinen1965c852009-04-22 21:38:25 +03006945}
6946
Samuel Ortizb23aa672009-07-01 21:26:54 +02006947void nl80211_send_connect_result(struct cfg80211_registered_device *rdev,
6948 struct net_device *netdev, const u8 *bssid,
6949 const u8 *req_ie, size_t req_ie_len,
6950 const u8 *resp_ie, size_t resp_ie_len,
6951 u16 status, gfp_t gfp)
6952{
6953 struct sk_buff *msg;
6954 void *hdr;
6955
6956 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6957 if (!msg)
6958 return;
6959
6960 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT);
6961 if (!hdr) {
6962 nlmsg_free(msg);
6963 return;
6964 }
6965
6966 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
6967 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
6968 if (bssid)
6969 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
6970 NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status);
6971 if (req_ie)
6972 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
6973 if (resp_ie)
6974 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
6975
Johannes Berg3b7b72e2011-10-22 19:05:51 +02006976 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006977
Johannes Berg463d0182009-07-14 00:33:35 +02006978 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
6979 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02006980 return;
6981
6982 nla_put_failure:
6983 genlmsg_cancel(msg, hdr);
6984 nlmsg_free(msg);
6985
6986}
6987
6988void nl80211_send_roamed(struct cfg80211_registered_device *rdev,
6989 struct net_device *netdev, const u8 *bssid,
6990 const u8 *req_ie, size_t req_ie_len,
6991 const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp)
6992{
6993 struct sk_buff *msg;
6994 void *hdr;
6995
6996 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
6997 if (!msg)
6998 return;
6999
7000 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM);
7001 if (!hdr) {
7002 nlmsg_free(msg);
7003 return;
7004 }
7005
7006 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7007 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7008 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7009 if (req_ie)
7010 NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie);
7011 if (resp_ie)
7012 NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie);
7013
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007014 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007015
Johannes Berg463d0182009-07-14 00:33:35 +02007016 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7017 nl80211_mlme_mcgrp.id, gfp);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007018 return;
7019
7020 nla_put_failure:
7021 genlmsg_cancel(msg, hdr);
7022 nlmsg_free(msg);
7023
7024}
7025
7026void nl80211_send_disconnected(struct cfg80211_registered_device *rdev,
7027 struct net_device *netdev, u16 reason,
Johannes Berg667503dd2009-07-07 03:56:11 +02007028 const u8 *ie, size_t ie_len, bool from_ap)
Samuel Ortizb23aa672009-07-01 21:26:54 +02007029{
7030 struct sk_buff *msg;
7031 void *hdr;
7032
Johannes Berg667503dd2009-07-07 03:56:11 +02007033 msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007034 if (!msg)
7035 return;
7036
7037 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT);
7038 if (!hdr) {
7039 nlmsg_free(msg);
7040 return;
7041 }
7042
7043 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7044 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7045 if (from_ap && reason)
7046 NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason);
7047 if (from_ap)
7048 NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP);
7049 if (ie)
7050 NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie);
7051
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007052 genlmsg_end(msg, hdr);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007053
Johannes Berg463d0182009-07-14 00:33:35 +02007054 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7055 nl80211_mlme_mcgrp.id, GFP_KERNEL);
Samuel Ortizb23aa672009-07-01 21:26:54 +02007056 return;
7057
7058 nla_put_failure:
7059 genlmsg_cancel(msg, hdr);
7060 nlmsg_free(msg);
7061
7062}
7063
Johannes Berg04a773a2009-04-19 21:24:32 +02007064void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev,
7065 struct net_device *netdev, const u8 *bssid,
7066 gfp_t gfp)
7067{
7068 struct sk_buff *msg;
7069 void *hdr;
7070
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007071 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007072 if (!msg)
7073 return;
7074
7075 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS);
7076 if (!hdr) {
7077 nlmsg_free(msg);
7078 return;
7079 }
7080
7081 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7082 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7083 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7084
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007085 genlmsg_end(msg, hdr);
Johannes Berg04a773a2009-04-19 21:24:32 +02007086
Johannes Berg463d0182009-07-14 00:33:35 +02007087 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7088 nl80211_mlme_mcgrp.id, gfp);
Johannes Berg04a773a2009-04-19 21:24:32 +02007089 return;
7090
7091 nla_put_failure:
7092 genlmsg_cancel(msg, hdr);
7093 nlmsg_free(msg);
7094}
7095
Javier Cardonac93b5e72011-04-07 15:08:34 -07007096void nl80211_send_new_peer_candidate(struct cfg80211_registered_device *rdev,
7097 struct net_device *netdev,
7098 const u8 *macaddr, const u8* ie, u8 ie_len,
7099 gfp_t gfp)
7100{
7101 struct sk_buff *msg;
7102 void *hdr;
7103
7104 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7105 if (!msg)
7106 return;
7107
7108 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NEW_PEER_CANDIDATE);
7109 if (!hdr) {
7110 nlmsg_free(msg);
7111 return;
7112 }
7113
7114 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7115 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7116 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, macaddr);
7117 if (ie_len && ie)
7118 NLA_PUT(msg, NL80211_ATTR_IE, ie_len , ie);
7119
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007120 genlmsg_end(msg, hdr);
Javier Cardonac93b5e72011-04-07 15:08:34 -07007121
7122 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7123 nl80211_mlme_mcgrp.id, gfp);
7124 return;
7125
7126 nla_put_failure:
7127 genlmsg_cancel(msg, hdr);
7128 nlmsg_free(msg);
7129}
7130
Jouni Malinena3b8b052009-03-27 21:59:49 +02007131void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev,
7132 struct net_device *netdev, const u8 *addr,
7133 enum nl80211_key_type key_type, int key_id,
Johannes Berge6d6e342009-07-01 21:26:47 +02007134 const u8 *tsc, gfp_t gfp)
Jouni Malinena3b8b052009-03-27 21:59:49 +02007135{
7136 struct sk_buff *msg;
7137 void *hdr;
7138
Johannes Berge6d6e342009-07-01 21:26:47 +02007139 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007140 if (!msg)
7141 return;
7142
7143 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_MICHAEL_MIC_FAILURE);
7144 if (!hdr) {
7145 nlmsg_free(msg);
7146 return;
7147 }
7148
7149 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7150 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7151 if (addr)
7152 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7153 NLA_PUT_U32(msg, NL80211_ATTR_KEY_TYPE, key_type);
Arik Nemtsova66b98d2011-06-23 00:00:24 +03007154 if (key_id != -1)
7155 NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_id);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007156 if (tsc)
7157 NLA_PUT(msg, NL80211_ATTR_KEY_SEQ, 6, tsc);
7158
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007159 genlmsg_end(msg, hdr);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007160
Johannes Berg463d0182009-07-14 00:33:35 +02007161 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7162 nl80211_mlme_mcgrp.id, gfp);
Jouni Malinena3b8b052009-03-27 21:59:49 +02007163 return;
7164
7165 nla_put_failure:
7166 genlmsg_cancel(msg, hdr);
7167 nlmsg_free(msg);
7168}
7169
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007170void nl80211_send_beacon_hint_event(struct wiphy *wiphy,
7171 struct ieee80211_channel *channel_before,
7172 struct ieee80211_channel *channel_after)
7173{
7174 struct sk_buff *msg;
7175 void *hdr;
7176 struct nlattr *nl_freq;
7177
Pablo Neira Ayusofd2120c2009-05-19 15:27:55 -07007178 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_ATOMIC);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007179 if (!msg)
7180 return;
7181
7182 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_REG_BEACON_HINT);
7183 if (!hdr) {
7184 nlmsg_free(msg);
7185 return;
7186 }
7187
7188 /*
7189 * Since we are applying the beacon hint to a wiphy we know its
7190 * wiphy_idx is valid
7191 */
7192 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, get_wiphy_idx(wiphy));
7193
7194 /* Before */
7195 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_BEFORE);
7196 if (!nl_freq)
7197 goto nla_put_failure;
7198 if (nl80211_msg_put_channel(msg, channel_before))
7199 goto nla_put_failure;
7200 nla_nest_end(msg, nl_freq);
7201
7202 /* After */
7203 nl_freq = nla_nest_start(msg, NL80211_ATTR_FREQ_AFTER);
7204 if (!nl_freq)
7205 goto nla_put_failure;
7206 if (nl80211_msg_put_channel(msg, channel_after))
7207 goto nla_put_failure;
7208 nla_nest_end(msg, nl_freq);
7209
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007210 genlmsg_end(msg, hdr);
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007211
Johannes Berg463d0182009-07-14 00:33:35 +02007212 rcu_read_lock();
7213 genlmsg_multicast_allns(msg, 0, nl80211_regulatory_mcgrp.id,
7214 GFP_ATOMIC);
7215 rcu_read_unlock();
Luis R. Rodriguez6bad8762009-04-02 14:08:09 -04007216
7217 return;
7218
7219nla_put_failure:
7220 genlmsg_cancel(msg, hdr);
7221 nlmsg_free(msg);
7222}
7223
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007224static void nl80211_send_remain_on_chan_event(
7225 int cmd, struct cfg80211_registered_device *rdev,
7226 struct net_device *netdev, u64 cookie,
7227 struct ieee80211_channel *chan,
7228 enum nl80211_channel_type channel_type,
7229 unsigned int duration, gfp_t gfp)
7230{
7231 struct sk_buff *msg;
7232 void *hdr;
7233
7234 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7235 if (!msg)
7236 return;
7237
7238 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
7239 if (!hdr) {
7240 nlmsg_free(msg);
7241 return;
7242 }
7243
7244 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7245 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7246 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq);
7247 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type);
7248 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7249
7250 if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL)
7251 NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration);
7252
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007253 genlmsg_end(msg, hdr);
Jouni Malinen9588bbd2009-12-23 13:15:41 +01007254
7255 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7256 nl80211_mlme_mcgrp.id, gfp);
7257 return;
7258
7259 nla_put_failure:
7260 genlmsg_cancel(msg, hdr);
7261 nlmsg_free(msg);
7262}
7263
7264void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev,
7265 struct net_device *netdev, u64 cookie,
7266 struct ieee80211_channel *chan,
7267 enum nl80211_channel_type channel_type,
7268 unsigned int duration, gfp_t gfp)
7269{
7270 nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL,
7271 rdev, netdev, cookie, chan,
7272 channel_type, duration, gfp);
7273}
7274
7275void nl80211_send_remain_on_channel_cancel(
7276 struct cfg80211_registered_device *rdev, struct net_device *netdev,
7277 u64 cookie, struct ieee80211_channel *chan,
7278 enum nl80211_channel_type channel_type, gfp_t gfp)
7279{
7280 nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL,
7281 rdev, netdev, cookie, chan,
7282 channel_type, 0, gfp);
7283}
7284
Johannes Berg98b62182009-12-23 13:15:44 +01007285void nl80211_send_sta_event(struct cfg80211_registered_device *rdev,
7286 struct net_device *dev, const u8 *mac_addr,
7287 struct station_info *sinfo, gfp_t gfp)
7288{
7289 struct sk_buff *msg;
7290
7291 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7292 if (!msg)
7293 return;
7294
7295 if (nl80211_send_station(msg, 0, 0, 0, dev, mac_addr, sinfo) < 0) {
7296 nlmsg_free(msg);
7297 return;
7298 }
7299
7300 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7301 nl80211_mlme_mcgrp.id, gfp);
7302}
7303
Jouni Malinenec15e682011-03-23 15:29:52 +02007304void nl80211_send_sta_del_event(struct cfg80211_registered_device *rdev,
7305 struct net_device *dev, const u8 *mac_addr,
7306 gfp_t gfp)
7307{
7308 struct sk_buff *msg;
7309 void *hdr;
7310
7311 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7312 if (!msg)
7313 return;
7314
7315 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DEL_STATION);
7316 if (!hdr) {
7317 nlmsg_free(msg);
7318 return;
7319 }
7320
7321 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7322 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
7323
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007324 genlmsg_end(msg, hdr);
Jouni Malinenec15e682011-03-23 15:29:52 +02007325
7326 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7327 nl80211_mlme_mcgrp.id, gfp);
7328 return;
7329
7330 nla_put_failure:
7331 genlmsg_cancel(msg, hdr);
7332 nlmsg_free(msg);
7333}
7334
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007335static bool __nl80211_unexpected_frame(struct net_device *dev, u8 cmd,
7336 const u8 *addr, gfp_t gfp)
Johannes Berg28946da2011-11-04 11:18:12 +01007337{
7338 struct wireless_dev *wdev = dev->ieee80211_ptr;
7339 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7340 struct sk_buff *msg;
7341 void *hdr;
7342 int err;
7343 u32 nlpid = ACCESS_ONCE(wdev->ap_unexpected_nlpid);
7344
7345 if (!nlpid)
7346 return false;
7347
7348 msg = nlmsg_new(100, gfp);
7349 if (!msg)
7350 return true;
7351
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007352 hdr = nl80211hdr_put(msg, 0, 0, 0, cmd);
Johannes Berg28946da2011-11-04 11:18:12 +01007353 if (!hdr) {
7354 nlmsg_free(msg);
7355 return true;
7356 }
7357
7358 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7359 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7360 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7361
7362 err = genlmsg_end(msg, hdr);
7363 if (err < 0) {
7364 nlmsg_free(msg);
7365 return true;
7366 }
7367
7368 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7369 return true;
7370
7371 nla_put_failure:
7372 genlmsg_cancel(msg, hdr);
7373 nlmsg_free(msg);
7374 return true;
7375}
7376
Johannes Bergb92ab5d2011-11-04 11:18:19 +01007377bool nl80211_unexpected_frame(struct net_device *dev, const u8 *addr, gfp_t gfp)
7378{
7379 return __nl80211_unexpected_frame(dev, NL80211_CMD_UNEXPECTED_FRAME,
7380 addr, gfp);
7381}
7382
7383bool nl80211_unexpected_4addr_frame(struct net_device *dev,
7384 const u8 *addr, gfp_t gfp)
7385{
7386 return __nl80211_unexpected_frame(dev,
7387 NL80211_CMD_UNEXPECTED_4ADDR_FRAME,
7388 addr, gfp);
7389}
7390
Johannes Berg2e161f72010-08-12 15:38:38 +02007391int nl80211_send_mgmt(struct cfg80211_registered_device *rdev,
7392 struct net_device *netdev, u32 nlpid,
7393 int freq, const u8 *buf, size_t len, gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007394{
7395 struct sk_buff *msg;
7396 void *hdr;
Jouni Malinen026331c2010-02-15 12:53:10 +02007397
7398 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7399 if (!msg)
7400 return -ENOMEM;
7401
Johannes Berg2e161f72010-08-12 15:38:38 +02007402 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
Jouni Malinen026331c2010-02-15 12:53:10 +02007403 if (!hdr) {
7404 nlmsg_free(msg);
7405 return -ENOMEM;
7406 }
7407
7408 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7409 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7410 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
7411 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7412
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007413 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007414
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007415 return genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
Jouni Malinen026331c2010-02-15 12:53:10 +02007416
7417 nla_put_failure:
7418 genlmsg_cancel(msg, hdr);
7419 nlmsg_free(msg);
7420 return -ENOBUFS;
7421}
7422
Johannes Berg2e161f72010-08-12 15:38:38 +02007423void nl80211_send_mgmt_tx_status(struct cfg80211_registered_device *rdev,
7424 struct net_device *netdev, u64 cookie,
7425 const u8 *buf, size_t len, bool ack,
7426 gfp_t gfp)
Jouni Malinen026331c2010-02-15 12:53:10 +02007427{
7428 struct sk_buff *msg;
7429 void *hdr;
7430
7431 msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp);
7432 if (!msg)
7433 return;
7434
Johannes Berg2e161f72010-08-12 15:38:38 +02007435 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME_TX_STATUS);
Jouni Malinen026331c2010-02-15 12:53:10 +02007436 if (!hdr) {
7437 nlmsg_free(msg);
7438 return;
7439 }
7440
7441 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7442 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7443 NLA_PUT(msg, NL80211_ATTR_FRAME, len, buf);
7444 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7445 if (ack)
7446 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7447
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007448 genlmsg_end(msg, hdr);
Jouni Malinen026331c2010-02-15 12:53:10 +02007449
7450 genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp);
7451 return;
7452
7453 nla_put_failure:
7454 genlmsg_cancel(msg, hdr);
7455 nlmsg_free(msg);
7456}
7457
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007458void
7459nl80211_send_cqm_rssi_notify(struct cfg80211_registered_device *rdev,
7460 struct net_device *netdev,
7461 enum nl80211_cqm_rssi_threshold_event rssi_event,
7462 gfp_t gfp)
7463{
7464 struct sk_buff *msg;
7465 struct nlattr *pinfoattr;
7466 void *hdr;
7467
7468 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7469 if (!msg)
7470 return;
7471
7472 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7473 if (!hdr) {
7474 nlmsg_free(msg);
7475 return;
7476 }
7477
7478 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7479 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7480
7481 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7482 if (!pinfoattr)
7483 goto nla_put_failure;
7484
7485 NLA_PUT_U32(msg, NL80211_ATTR_CQM_RSSI_THRESHOLD_EVENT,
7486 rssi_event);
7487
7488 nla_nest_end(msg, pinfoattr);
7489
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007490 genlmsg_end(msg, hdr);
Juuso Oikarinend6dc1a32010-03-23 09:02:33 +02007491
7492 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7493 nl80211_mlme_mcgrp.id, gfp);
7494 return;
7495
7496 nla_put_failure:
7497 genlmsg_cancel(msg, hdr);
7498 nlmsg_free(msg);
7499}
7500
Johannes Berge5497d72011-07-05 16:35:40 +02007501void nl80211_gtk_rekey_notify(struct cfg80211_registered_device *rdev,
7502 struct net_device *netdev, const u8 *bssid,
7503 const u8 *replay_ctr, gfp_t gfp)
7504{
7505 struct sk_buff *msg;
7506 struct nlattr *rekey_attr;
7507 void *hdr;
7508
7509 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7510 if (!msg)
7511 return;
7512
7513 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_SET_REKEY_OFFLOAD);
7514 if (!hdr) {
7515 nlmsg_free(msg);
7516 return;
7517 }
7518
7519 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7520 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7521 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid);
7522
7523 rekey_attr = nla_nest_start(msg, NL80211_ATTR_REKEY_DATA);
7524 if (!rekey_attr)
7525 goto nla_put_failure;
7526
7527 NLA_PUT(msg, NL80211_REKEY_DATA_REPLAY_CTR,
7528 NL80211_REPLAY_CTR_LEN, replay_ctr);
7529
7530 nla_nest_end(msg, rekey_attr);
7531
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007532 genlmsg_end(msg, hdr);
Johannes Berge5497d72011-07-05 16:35:40 +02007533
7534 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7535 nl80211_mlme_mcgrp.id, gfp);
7536 return;
7537
7538 nla_put_failure:
7539 genlmsg_cancel(msg, hdr);
7540 nlmsg_free(msg);
7541}
7542
Jouni Malinenc9df56b2011-09-16 18:56:23 +03007543void nl80211_pmksa_candidate_notify(struct cfg80211_registered_device *rdev,
7544 struct net_device *netdev, int index,
7545 const u8 *bssid, bool preauth, gfp_t gfp)
7546{
7547 struct sk_buff *msg;
7548 struct nlattr *attr;
7549 void *hdr;
7550
7551 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7552 if (!msg)
7553 return;
7554
7555 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PMKSA_CANDIDATE);
7556 if (!hdr) {
7557 nlmsg_free(msg);
7558 return;
7559 }
7560
7561 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7562 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7563
7564 attr = nla_nest_start(msg, NL80211_ATTR_PMKSA_CANDIDATE);
7565 if (!attr)
7566 goto nla_put_failure;
7567
7568 NLA_PUT_U32(msg, NL80211_PMKSA_CANDIDATE_INDEX, index);
7569 NLA_PUT(msg, NL80211_PMKSA_CANDIDATE_BSSID, ETH_ALEN, bssid);
7570 if (preauth)
7571 NLA_PUT_FLAG(msg, NL80211_PMKSA_CANDIDATE_PREAUTH);
7572
7573 nla_nest_end(msg, attr);
7574
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007575 genlmsg_end(msg, hdr);
Jouni Malinenc9df56b2011-09-16 18:56:23 +03007576
7577 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7578 nl80211_mlme_mcgrp.id, gfp);
7579 return;
7580
7581 nla_put_failure:
7582 genlmsg_cancel(msg, hdr);
7583 nlmsg_free(msg);
7584}
7585
Johannes Bergc063dbf2010-11-24 08:10:05 +01007586void
7587nl80211_send_cqm_pktloss_notify(struct cfg80211_registered_device *rdev,
7588 struct net_device *netdev, const u8 *peer,
7589 u32 num_packets, gfp_t gfp)
7590{
7591 struct sk_buff *msg;
7592 struct nlattr *pinfoattr;
7593 void *hdr;
7594
7595 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7596 if (!msg)
7597 return;
7598
7599 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_NOTIFY_CQM);
7600 if (!hdr) {
7601 nlmsg_free(msg);
7602 return;
7603 }
7604
7605 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7606 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex);
7607 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, peer);
7608
7609 pinfoattr = nla_nest_start(msg, NL80211_ATTR_CQM);
7610 if (!pinfoattr)
7611 goto nla_put_failure;
7612
7613 NLA_PUT_U32(msg, NL80211_ATTR_CQM_PKT_LOSS_EVENT, num_packets);
7614
7615 nla_nest_end(msg, pinfoattr);
7616
Johannes Berg3b7b72e2011-10-22 19:05:51 +02007617 genlmsg_end(msg, hdr);
Johannes Bergc063dbf2010-11-24 08:10:05 +01007618
7619 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7620 nl80211_mlme_mcgrp.id, gfp);
7621 return;
7622
7623 nla_put_failure:
7624 genlmsg_cancel(msg, hdr);
7625 nlmsg_free(msg);
7626}
7627
Johannes Berg7f6cf312011-11-04 11:18:15 +01007628void cfg80211_probe_status(struct net_device *dev, const u8 *addr,
7629 u64 cookie, bool acked, gfp_t gfp)
7630{
7631 struct wireless_dev *wdev = dev->ieee80211_ptr;
7632 struct cfg80211_registered_device *rdev = wiphy_to_dev(wdev->wiphy);
7633 struct sk_buff *msg;
7634 void *hdr;
7635 int err;
7636
7637 msg = nlmsg_new(NLMSG_GOODSIZE, gfp);
7638 if (!msg)
7639 return;
7640
7641 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_PROBE_CLIENT);
7642 if (!hdr) {
7643 nlmsg_free(msg);
7644 return;
7645 }
7646
7647 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7648 NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
7649 NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, addr);
7650 NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie);
7651 if (acked)
7652 NLA_PUT_FLAG(msg, NL80211_ATTR_ACK);
7653
7654 err = genlmsg_end(msg, hdr);
7655 if (err < 0) {
7656 nlmsg_free(msg);
7657 return;
7658 }
7659
7660 genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0,
7661 nl80211_mlme_mcgrp.id, gfp);
7662 return;
7663
7664 nla_put_failure:
7665 genlmsg_cancel(msg, hdr);
7666 nlmsg_free(msg);
7667}
7668EXPORT_SYMBOL(cfg80211_probe_status);
7669
Johannes Berg5e7602302011-11-04 11:18:17 +01007670void cfg80211_report_obss_beacon(struct wiphy *wiphy,
7671 const u8 *frame, size_t len,
7672 int freq, gfp_t gfp)
7673{
7674 struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy);
7675 struct sk_buff *msg;
7676 void *hdr;
7677 u32 nlpid = ACCESS_ONCE(rdev->ap_beacons_nlpid);
7678
7679 if (!nlpid)
7680 return;
7681
7682 msg = nlmsg_new(len + 100, gfp);
7683 if (!msg)
7684 return;
7685
7686 hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_FRAME);
7687 if (!hdr) {
7688 nlmsg_free(msg);
7689 return;
7690 }
7691
7692 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx);
7693 if (freq)
7694 NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, freq);
7695 NLA_PUT(msg, NL80211_ATTR_FRAME, len, frame);
7696
7697 genlmsg_end(msg, hdr);
7698
7699 genlmsg_unicast(wiphy_net(&rdev->wiphy), msg, nlpid);
7700 return;
7701
7702 nla_put_failure:
7703 genlmsg_cancel(msg, hdr);
7704 nlmsg_free(msg);
7705}
7706EXPORT_SYMBOL(cfg80211_report_obss_beacon);
7707
Jouni Malinen026331c2010-02-15 12:53:10 +02007708static int nl80211_netlink_notify(struct notifier_block * nb,
7709 unsigned long state,
7710 void *_notify)
7711{
7712 struct netlink_notify *notify = _notify;
7713 struct cfg80211_registered_device *rdev;
7714 struct wireless_dev *wdev;
7715
7716 if (state != NETLINK_URELEASE)
7717 return NOTIFY_DONE;
7718
7719 rcu_read_lock();
7720
Johannes Berg5e7602302011-11-04 11:18:17 +01007721 list_for_each_entry_rcu(rdev, &cfg80211_rdev_list, list) {
Jouni Malinen026331c2010-02-15 12:53:10 +02007722 list_for_each_entry_rcu(wdev, &rdev->netdev_list, list)
Johannes Berg2e161f72010-08-12 15:38:38 +02007723 cfg80211_mlme_unregister_socket(wdev, notify->pid);
Johannes Berg5e7602302011-11-04 11:18:17 +01007724 if (rdev->ap_beacons_nlpid == notify->pid)
7725 rdev->ap_beacons_nlpid = 0;
7726 }
Jouni Malinen026331c2010-02-15 12:53:10 +02007727
7728 rcu_read_unlock();
7729
7730 return NOTIFY_DONE;
7731}
7732
7733static struct notifier_block nl80211_netlink_notifier = {
7734 .notifier_call = nl80211_netlink_notify,
7735};
7736
Johannes Berg55682962007-09-20 13:09:35 -04007737/* initialisation/exit functions */
7738
7739int nl80211_init(void)
7740{
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00007741 int err;
Johannes Berg55682962007-09-20 13:09:35 -04007742
Michał Mirosław0d63cbb2009-05-21 10:34:06 +00007743 err = genl_register_family_with_ops(&nl80211_fam,
7744 nl80211_ops, ARRAY_SIZE(nl80211_ops));
Johannes Berg55682962007-09-20 13:09:35 -04007745 if (err)
7746 return err;
7747
Johannes Berg55682962007-09-20 13:09:35 -04007748 err = genl_register_mc_group(&nl80211_fam, &nl80211_config_mcgrp);
7749 if (err)
7750 goto err_out;
7751
Johannes Berg2a519312009-02-10 21:25:55 +01007752 err = genl_register_mc_group(&nl80211_fam, &nl80211_scan_mcgrp);
7753 if (err)
7754 goto err_out;
7755
Luis R. Rodriguez73d54c92009-03-09 22:07:42 -04007756 err = genl_register_mc_group(&nl80211_fam, &nl80211_regulatory_mcgrp);
7757 if (err)
7758 goto err_out;
7759
Jouni Malinen6039f6d2009-03-19 13:39:21 +02007760 err = genl_register_mc_group(&nl80211_fam, &nl80211_mlme_mcgrp);
7761 if (err)
7762 goto err_out;
7763
Johannes Bergaff89a92009-07-01 21:26:51 +02007764#ifdef CONFIG_NL80211_TESTMODE
7765 err = genl_register_mc_group(&nl80211_fam, &nl80211_testmode_mcgrp);
7766 if (err)
7767 goto err_out;
7768#endif
7769
Jouni Malinen026331c2010-02-15 12:53:10 +02007770 err = netlink_register_notifier(&nl80211_netlink_notifier);
7771 if (err)
7772 goto err_out;
7773
Johannes Berg55682962007-09-20 13:09:35 -04007774 return 0;
7775 err_out:
7776 genl_unregister_family(&nl80211_fam);
7777 return err;
7778}
7779
7780void nl80211_exit(void)
7781{
Jouni Malinen026331c2010-02-15 12:53:10 +02007782 netlink_unregister_notifier(&nl80211_netlink_notifier);
Johannes Berg55682962007-09-20 13:09:35 -04007783 genl_unregister_family(&nl80211_fam);
7784}