aboutsummaryrefslogtreecommitdiffstats
path: root/net/mac80211/cfg.c
diff options
context:
space:
mode:
authorArik Nemtsov <arik@wizery.com>2011-09-28 07:12:52 -0400
committerJohn W. Linville <linville@tuxdriver.com>2011-09-30 15:57:07 -0400
commitdfe018bf99537e42c816d3f543620a7e09fcf3cd (patch)
tree9efb4a6d078bb1051ba9dae9342b7685943a3c48 /net/mac80211/cfg.c
parent768db3438b4b48a33d073093bb364e624409cab7 (diff)
mac80211: handle TDLS high-level commands and frames
Register and implement the TDLS cfg80211 callback functions. Internally prepare and send TDLS management frames. We incorporate local STA capabilities and supported rates with extra IEs given by usermode. The resulting packet is either encapsulated in a data frame, or assembled as an action frame. It is transmitted either directly or through the AP, as mandated by the TDLS specification. Declare support for the TDLS external setup wiphy capability. This tells usermode to handle link setup and discovery on its own, and use the kernel driver for sending TDLS mgmt packets. Signed-off-by: Arik Nemtsov <arik@wizery.com> Cc: Kalyan C Gaddam <chakkal@iit.edu> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r--net/mac80211/cfg.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 13061ebc93ef..1d17677a0ec1 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -12,6 +12,7 @@
12#include <linux/slab.h> 12#include <linux/slab.h>
13#include <net/net_namespace.h> 13#include <net/net_namespace.h>
14#include <linux/rcupdate.h> 14#include <linux/rcupdate.h>
15#include <linux/if_ether.h>
15#include <net/cfg80211.h> 16#include <net/cfg80211.h>
16#include "ieee80211_i.h" 17#include "ieee80211_i.h"
17#include "driver-ops.h" 18#include "driver-ops.h"
@@ -2128,6 +2129,313 @@ static int ieee80211_set_rekey_data(struct wiphy *wiphy,
2128 return 0; 2129 return 0;
2129} 2130}
2130 2131
2132static void ieee80211_tdls_add_ext_capab(struct sk_buff *skb)
2133{
2134 u8 *pos = (void *)skb_put(skb, 7);
2135
2136 *pos++ = WLAN_EID_EXT_CAPABILITY;
2137 *pos++ = 5; /* len */
2138 *pos++ = 0x0;
2139 *pos++ = 0x0;
2140 *pos++ = 0x0;
2141 *pos++ = 0x0;
2142 *pos++ = WLAN_EXT_CAPA5_TDLS_ENABLED;
2143}
2144
2145static u16 ieee80211_get_tdls_sta_capab(struct ieee80211_sub_if_data *sdata)
2146{
2147 struct ieee80211_local *local = sdata->local;
2148 u16 capab;
2149
2150 capab = 0;
2151 if (local->oper_channel->band != IEEE80211_BAND_2GHZ)
2152 return capab;
2153
2154 if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_SLOT_INCAPABLE))
2155 capab |= WLAN_CAPABILITY_SHORT_SLOT_TIME;
2156 if (!(local->hw.flags & IEEE80211_HW_2GHZ_SHORT_PREAMBLE_INCAPABLE))
2157 capab |= WLAN_CAPABILITY_SHORT_PREAMBLE;
2158
2159 return capab;
2160}
2161
2162static void ieee80211_tdls_add_link_ie(struct sk_buff *skb, u8 *src_addr,
2163 u8 *peer, u8 *bssid)
2164{
2165 struct ieee80211_tdls_lnkie *lnkid;
2166
2167 lnkid = (void *)skb_put(skb, sizeof(struct ieee80211_tdls_lnkie));
2168
2169 lnkid->ie_type = WLAN_EID_LINK_ID;
2170 lnkid->ie_len = sizeof(struct ieee80211_tdls_lnkie) - 2;
2171
2172 memcpy(lnkid->bssid, bssid, ETH_ALEN);
2173 memcpy(lnkid->init_sta, src_addr, ETH_ALEN);
2174 memcpy(lnkid->resp_sta, peer, ETH_ALEN);
2175}
2176
2177static int
2178ieee80211_prep_tdls_encap_data(struct wiphy *wiphy, struct net_device *dev,
2179 u8 *peer, u8 action_code, u8 dialog_token,
2180 u16 status_code, struct sk_buff *skb)
2181{
2182 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2183 struct ieee80211_tdls_data *tf;
2184
2185 tf = (void *)skb_put(skb, offsetof(struct ieee80211_tdls_data, u));
2186
2187 memcpy(tf->da, peer, ETH_ALEN);
2188 memcpy(tf->sa, sdata->vif.addr, ETH_ALEN);
2189 tf->ether_type = cpu_to_be16(ETH_P_TDLS);
2190 tf->payload_type = WLAN_TDLS_SNAP_RFTYPE;
2191
2192 switch (action_code) {
2193 case WLAN_TDLS_SETUP_REQUEST:
2194 tf->category = WLAN_CATEGORY_TDLS;
2195 tf->action_code = WLAN_TDLS_SETUP_REQUEST;
2196
2197 skb_put(skb, sizeof(tf->u.setup_req));
2198 tf->u.setup_req.dialog_token = dialog_token;
2199 tf->u.setup_req.capability =
2200 cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
2201
2202 ieee80211_add_srates_ie(&sdata->vif, skb);
2203 ieee80211_add_ext_srates_ie(&sdata->vif, skb);
2204 ieee80211_tdls_add_ext_capab(skb);
2205 break;
2206 case WLAN_TDLS_SETUP_RESPONSE:
2207 tf->category = WLAN_CATEGORY_TDLS;
2208 tf->action_code = WLAN_TDLS_SETUP_RESPONSE;
2209
2210 skb_put(skb, sizeof(tf->u.setup_resp));
2211 tf->u.setup_resp.status_code = cpu_to_le16(status_code);
2212 tf->u.setup_resp.dialog_token = dialog_token;
2213 tf->u.setup_resp.capability =
2214 cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
2215
2216 ieee80211_add_srates_ie(&sdata->vif, skb);
2217 ieee80211_add_ext_srates_ie(&sdata->vif, skb);
2218 ieee80211_tdls_add_ext_capab(skb);
2219 break;
2220 case WLAN_TDLS_SETUP_CONFIRM:
2221 tf->category = WLAN_CATEGORY_TDLS;
2222 tf->action_code = WLAN_TDLS_SETUP_CONFIRM;
2223
2224 skb_put(skb, sizeof(tf->u.setup_cfm));
2225 tf->u.setup_cfm.status_code = cpu_to_le16(status_code);
2226 tf->u.setup_cfm.dialog_token = dialog_token;
2227 break;
2228 case WLAN_TDLS_TEARDOWN:
2229 tf->category = WLAN_CATEGORY_TDLS;
2230 tf->action_code = WLAN_TDLS_TEARDOWN;
2231
2232 skb_put(skb, sizeof(tf->u.teardown));
2233 tf->u.teardown.reason_code = cpu_to_le16(status_code);
2234 break;
2235 case WLAN_TDLS_DISCOVERY_REQUEST:
2236 tf->category = WLAN_CATEGORY_TDLS;
2237 tf->action_code = WLAN_TDLS_DISCOVERY_REQUEST;
2238
2239 skb_put(skb, sizeof(tf->u.discover_req));
2240 tf->u.discover_req.dialog_token = dialog_token;
2241 break;
2242 default:
2243 return -EINVAL;
2244 }
2245
2246 return 0;
2247}
2248
2249static int
2250ieee80211_prep_tdls_direct(struct wiphy *wiphy, struct net_device *dev,
2251 u8 *peer, u8 action_code, u8 dialog_token,
2252 u16 status_code, struct sk_buff *skb)
2253{
2254 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2255 struct ieee80211_mgmt *mgmt;
2256
2257 mgmt = (void *)skb_put(skb, 24);
2258 memset(mgmt, 0, 24);
2259 memcpy(mgmt->da, peer, ETH_ALEN);
2260 memcpy(mgmt->sa, sdata->vif.addr, ETH_ALEN);
2261 memcpy(mgmt->bssid, sdata->u.mgd.bssid, ETH_ALEN);
2262
2263 mgmt->frame_control = cpu_to_le16(IEEE80211_FTYPE_MGMT |
2264 IEEE80211_STYPE_ACTION);
2265
2266 switch (action_code) {
2267 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
2268 skb_put(skb, 1 + sizeof(mgmt->u.action.u.tdls_discover_resp));
2269 mgmt->u.action.category = WLAN_CATEGORY_PUBLIC;
2270 mgmt->u.action.u.tdls_discover_resp.action_code =
2271 WLAN_PUB_ACTION_TDLS_DISCOVER_RES;
2272 mgmt->u.action.u.tdls_discover_resp.dialog_token =
2273 dialog_token;
2274 mgmt->u.action.u.tdls_discover_resp.capability =
2275 cpu_to_le16(ieee80211_get_tdls_sta_capab(sdata));
2276
2277 ieee80211_add_srates_ie(&sdata->vif, skb);
2278 ieee80211_add_ext_srates_ie(&sdata->vif, skb);
2279 ieee80211_tdls_add_ext_capab(skb);
2280 break;
2281 default:
2282 return -EINVAL;
2283 }
2284
2285 return 0;
2286}
2287
2288static int ieee80211_tdls_mgmt(struct wiphy *wiphy, struct net_device *dev,
2289 u8 *peer, u8 action_code, u8 dialog_token,
2290 u16 status_code, const u8 *extra_ies,
2291 size_t extra_ies_len)
2292{
2293 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2294 struct ieee80211_local *local = sdata->local;
2295 struct ieee80211_tx_info *info;
2296 struct sk_buff *skb = NULL;
2297 bool send_direct;
2298 int ret;
2299
2300 if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
2301 return -ENOTSUPP;
2302
2303 /* make sure we are in managed mode, and associated */
2304 if (sdata->vif.type != NL80211_IFTYPE_STATION ||
2305 !sdata->u.mgd.associated)
2306 return -EINVAL;
2307
2308#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG
2309 printk(KERN_DEBUG "TDLS mgmt action %d peer %pM\n", action_code, peer);
2310#endif
2311
2312 skb = dev_alloc_skb(local->hw.extra_tx_headroom +
2313 max(sizeof(struct ieee80211_mgmt),
2314 sizeof(struct ieee80211_tdls_data)) +
2315 50 + /* supported rates */
2316 7 + /* ext capab */
2317 extra_ies_len +
2318 sizeof(struct ieee80211_tdls_lnkie));
2319 if (!skb)
2320 return -ENOMEM;
2321
2322 info = IEEE80211_SKB_CB(skb);
2323 skb_reserve(skb, local->hw.extra_tx_headroom);
2324
2325 switch (action_code) {
2326 case WLAN_TDLS_SETUP_REQUEST:
2327 case WLAN_TDLS_SETUP_RESPONSE:
2328 case WLAN_TDLS_SETUP_CONFIRM:
2329 case WLAN_TDLS_TEARDOWN:
2330 case WLAN_TDLS_DISCOVERY_REQUEST:
2331 ret = ieee80211_prep_tdls_encap_data(wiphy, dev, peer,
2332 action_code, dialog_token,
2333 status_code, skb);
2334 send_direct = false;
2335 break;
2336 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
2337 ret = ieee80211_prep_tdls_direct(wiphy, dev, peer, action_code,
2338 dialog_token, status_code,
2339 skb);
2340 send_direct = true;
2341 break;
2342 default:
2343 ret = -ENOTSUPP;
2344 break;
2345 }
2346
2347 if (ret < 0)
2348 goto fail;
2349
2350 if (extra_ies_len)
2351 memcpy(skb_put(skb, extra_ies_len), extra_ies, extra_ies_len);
2352
2353 /* the TDLS link IE is always added last */
2354 switch (action_code) {
2355 case WLAN_TDLS_SETUP_REQUEST:
2356 case WLAN_TDLS_SETUP_CONFIRM:
2357 case WLAN_TDLS_TEARDOWN:
2358 case WLAN_TDLS_DISCOVERY_REQUEST:
2359 /* we are the initiator */
2360 ieee80211_tdls_add_link_ie(skb, sdata->vif.addr, peer,
2361 sdata->u.mgd.bssid);
2362 break;
2363 case WLAN_TDLS_SETUP_RESPONSE:
2364 case WLAN_PUB_ACTION_TDLS_DISCOVER_RES:
2365 /* we are the responder */
2366 ieee80211_tdls_add_link_ie(skb, peer, sdata->vif.addr,
2367 sdata->u.mgd.bssid);
2368 break;
2369 default:
2370 ret = -ENOTSUPP;
2371 goto fail;
2372 }
2373
2374 if (send_direct) {
2375 ieee80211_tx_skb(sdata, skb);
2376 return 0;
2377 }
2378
2379 /*
2380 * According to 802.11z: Setup req/resp are sent in AC_BK, otherwise
2381 * we should default to AC_VI.
2382 */
2383 switch (action_code) {
2384 case WLAN_TDLS_SETUP_REQUEST:
2385 case WLAN_TDLS_SETUP_RESPONSE:
2386 skb_set_queue_mapping(skb, IEEE80211_AC_BK);
2387 skb->priority = 2;
2388 break;
2389 default:
2390 skb_set_queue_mapping(skb, IEEE80211_AC_VI);
2391 skb->priority = 5;
2392 break;
2393 }
2394
2395 /* disable bottom halves when entering the Tx path */
2396 local_bh_disable();
2397 ret = ieee80211_subif_start_xmit(skb, dev);
2398 local_bh_enable();
2399
2400 return ret;
2401
2402fail:
2403 dev_kfree_skb(skb);
2404 return ret;
2405}
2406
2407static int ieee80211_tdls_oper(struct wiphy *wiphy, struct net_device *dev,
2408 u8 *peer, enum nl80211_tdls_operation oper)
2409{
2410 struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
2411
2412 if (!(wiphy->flags & WIPHY_FLAG_SUPPORTS_TDLS))
2413 return -ENOTSUPP;
2414
2415 if (sdata->vif.type != NL80211_IFTYPE_STATION)
2416 return -EINVAL;
2417
2418#ifdef CONFIG_MAC80211_VERBOSE_TDLS_DEBUG
2419 printk(KERN_DEBUG "TDLS oper %d peer %pM\n", oper, peer);
2420#endif
2421
2422 switch (oper) {
2423 case NL80211_TDLS_ENABLE_LINK:
2424 break;
2425 case NL80211_TDLS_DISABLE_LINK:
2426 return sta_info_destroy_addr(sdata, peer);
2427 case NL80211_TDLS_TEARDOWN:
2428 case NL80211_TDLS_SETUP:
2429 case NL80211_TDLS_DISCOVERY_REQ:
2430 /* We don't support in-driver setup/teardown/discovery */
2431 return -ENOTSUPP;
2432 default:
2433 return -ENOTSUPP;
2434 }
2435
2436 return 0;
2437}
2438
2131struct cfg80211_ops mac80211_config_ops = { 2439struct cfg80211_ops mac80211_config_ops = {
2132 .add_virtual_intf = ieee80211_add_iface, 2440 .add_virtual_intf = ieee80211_add_iface,
2133 .del_virtual_intf = ieee80211_del_iface, 2441 .del_virtual_intf = ieee80211_del_iface,
@@ -2191,4 +2499,6 @@ struct cfg80211_ops mac80211_config_ops = {
2191 .set_ringparam = ieee80211_set_ringparam, 2499 .set_ringparam = ieee80211_set_ringparam,
2192 .get_ringparam = ieee80211_get_ringparam, 2500 .get_ringparam = ieee80211_get_ringparam,
2193 .set_rekey_data = ieee80211_set_rekey_data, 2501 .set_rekey_data = ieee80211_set_rekey_data,
2502 .tdls_oper = ieee80211_tdls_oper,
2503 .tdls_mgmt = ieee80211_tdls_mgmt,
2194}; 2504};