2 * Copyright 2002-2005, Instant802 Networks, Inc.
3 * Copyright 2005-2006, Devicescape Software, Inc.
4 * Copyright 2006-2007 Jiri Benc <jbenc@suse.cz>
5 * Copyright 2007 Johannes Berg <johannes@sipsolutions.net>
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
12 #include <linux/if_ether.h>
13 #include <linux/etherdevice.h>
14 #include <linux/list.h>
15 #include <net/mac80211.h>
16 #include "ieee80211_i.h"
17 #include "debugfs_key.h"
24 * Key handling in mac80211 is done based on per-interface (sub_if_data)
25 * keys and per-station keys. Since each station belongs to an interface,
26 * each station key also belongs to that interface.
28 * Hardware acceleration is done on a best-effort basis, for each key
29 * that is eligible the hardware is asked to enable that key but if
30 * it cannot do that they key is simply kept for software encryption.
31 * There is currently no way of knowing this except by looking into
34 * All operations here are called under RTNL so no extra locking is
38 static const u8 bcast_addr
[ETH_ALEN
] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
39 static const u8 zero_addr
[ETH_ALEN
];
41 static const u8
*get_mac_for_key(struct ieee80211_key
*key
)
43 const u8
*addr
= bcast_addr
;
46 * If we're an AP we won't ever receive frames with a non-WEP
47 * group key so we tell the driver that by using the zero MAC
48 * address to indicate a transmit-only key.
50 if (key
->conf
.alg
!= ALG_WEP
&&
51 (key
->sdata
->type
== IEEE80211_IF_TYPE_AP
||
52 key
->sdata
->type
== IEEE80211_IF_TYPE_VLAN
))
56 addr
= key
->sta
->addr
;
61 static void ieee80211_key_enable_hw_accel(struct ieee80211_key
*key
)
66 if (!key
->local
->ops
->set_key
)
69 addr
= get_mac_for_key(key
);
71 ret
= key
->local
->ops
->set_key(local_to_hw(key
->local
), SET_KEY
,
72 key
->sdata
->dev
->dev_addr
, addr
,
75 WARN_ON(!ret
&& (key
->conf
.hw_key_idx
== HW_KEY_IDX_INVALID
));
78 key
->flags
|= KEY_FLAG_UPLOADED_TO_HARDWARE
;
80 if (ret
&& ret
!= -ENOSPC
&& ret
!= -EOPNOTSUPP
)
81 printk(KERN_ERR
"mac80211-%s: failed to set key "
82 "(%d, " MAC_FMT
") to hardware (%d)\n",
83 wiphy_name(key
->local
->hw
.wiphy
),
84 key
->conf
.keyidx
, MAC_ARG(addr
), ret
);
87 static void ieee80211_key_disable_hw_accel(struct ieee80211_key
*key
)
92 if (!key
->local
->ops
->set_key
)
95 if (!(key
->flags
& KEY_FLAG_UPLOADED_TO_HARDWARE
))
98 addr
= get_mac_for_key(key
);
100 ret
= key
->local
->ops
->set_key(local_to_hw(key
->local
), DISABLE_KEY
,
101 key
->sdata
->dev
->dev_addr
, addr
,
105 printk(KERN_ERR
"mac80211-%s: failed to remove key "
106 "(%d, " MAC_FMT
") from hardware (%d)\n",
107 wiphy_name(key
->local
->hw
.wiphy
),
108 key
->conf
.keyidx
, MAC_ARG(addr
), ret
);
110 key
->flags
&= ~KEY_FLAG_UPLOADED_TO_HARDWARE
;
111 key
->conf
.hw_key_idx
= HW_KEY_IDX_INVALID
;
114 struct ieee80211_key
*ieee80211_key_alloc(struct ieee80211_sub_if_data
*sdata
,
115 struct sta_info
*sta
,
116 ieee80211_key_alg alg
,
121 struct ieee80211_key
*key
;
123 BUG_ON(alg
== ALG_NONE
);
125 key
= kzalloc(sizeof(struct ieee80211_key
) + key_len
, GFP_KERNEL
);
130 * Default to software encryption; we'll later upload the
131 * key to the hardware if possible.
133 key
->conf
.hw_key_idx
= HW_KEY_IDX_INVALID
;
138 key
->conf
.keyidx
= idx
;
139 key
->conf
.keylen
= key_len
;
140 memcpy(key
->conf
.key
, key_data
, key_len
);
142 key
->local
= sdata
->local
;
146 if (alg
== ALG_CCMP
) {
148 * Initialize AES key state here as an optimization so that
149 * it does not need to be initialized for every packet.
151 key
->u
.ccmp
.tfm
= ieee80211_aes_key_setup_encrypt(key_data
);
152 if (!key
->u
.ccmp
.tfm
) {
153 ieee80211_key_free(key
);
158 ieee80211_debugfs_key_add(key
->local
, key
);
161 ieee80211_debugfs_key_sta_link(key
, sta
);
164 * some hardware cannot handle TKIP with QoS, so
165 * we indicate whether QoS could be in use.
167 if (sta
->flags
& WLAN_STA_WME
)
168 key
->conf
.flags
|= IEEE80211_KEY_FLAG_WMM_STA
;
170 if (sdata
->type
== IEEE80211_IF_TYPE_STA
) {
173 /* same here, the AP could be using QoS */
174 ap
= sta_info_get(key
->local
, key
->sdata
->u
.sta
.bssid
);
176 if (ap
->flags
& WLAN_STA_WME
)
178 IEEE80211_KEY_FLAG_WMM_STA
;
183 if (idx
>= 0 && idx
< NUM_DEFAULT_KEYS
) {
184 if (!sdata
->keys
[idx
])
185 sdata
->keys
[idx
] = key
;
192 list_add(&key
->list
, &sdata
->key_list
);
194 if (netif_running(key
->sdata
->dev
))
195 ieee80211_key_enable_hw_accel(key
);
200 void ieee80211_key_free(struct ieee80211_key
*key
)
205 ieee80211_key_disable_hw_accel(key
);
208 key
->sta
->key
= NULL
;
210 if (key
->sdata
->default_key
== key
)
211 ieee80211_set_default_key(key
->sdata
, -1);
212 if (key
->conf
.keyidx
>= 0 &&
213 key
->conf
.keyidx
< NUM_DEFAULT_KEYS
)
214 key
->sdata
->keys
[key
->conf
.keyidx
] = NULL
;
219 if (key
->conf
.alg
== ALG_CCMP
)
220 ieee80211_aes_key_free(key
->u
.ccmp
.tfm
);
221 ieee80211_debugfs_key_remove(key
);
223 list_del(&key
->list
);
228 void ieee80211_set_default_key(struct ieee80211_sub_if_data
*sdata
, int idx
)
230 struct ieee80211_key
*key
= NULL
;
232 if (idx
>= 0 && idx
< NUM_DEFAULT_KEYS
)
233 key
= sdata
->keys
[idx
];
235 if (sdata
->default_key
!= key
) {
236 ieee80211_debugfs_key_remove_default(sdata
);
238 sdata
->default_key
= key
;
240 if (sdata
->default_key
)
241 ieee80211_debugfs_key_add_default(sdata
);
243 if (sdata
->local
->ops
->set_key_idx
)
244 sdata
->local
->ops
->set_key_idx(
245 local_to_hw(sdata
->local
), idx
);
249 void ieee80211_free_keys(struct ieee80211_sub_if_data
*sdata
)
251 struct ieee80211_key
*key
, *tmp
;
253 list_for_each_entry_safe(key
, tmp
, &sdata
->key_list
, list
)
254 ieee80211_key_free(key
);
257 void ieee80211_enable_keys(struct ieee80211_sub_if_data
*sdata
)
259 struct ieee80211_key
*key
;
261 WARN_ON(!netif_running(sdata
->dev
));
262 if (!netif_running(sdata
->dev
))
265 list_for_each_entry(key
, &sdata
->key_list
, list
)
266 ieee80211_key_enable_hw_accel(key
);
269 void ieee80211_disable_keys(struct ieee80211_sub_if_data
*sdata
)
271 struct ieee80211_key
*key
;
273 list_for_each_entry(key
, &sdata
->key_list
, list
)
274 ieee80211_key_disable_hw_accel(key
);