diff options
Diffstat (limited to 'net/mac80211/cfg.c')
-rw-r--r-- | net/mac80211/cfg.c | 458 |
1 files changed, 388 insertions, 70 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index 22c9619ba776..699d97b8de5e 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -14,7 +14,8 @@ | |||
14 | #include <net/cfg80211.h> | 14 | #include <net/cfg80211.h> |
15 | #include "ieee80211_i.h" | 15 | #include "ieee80211_i.h" |
16 | #include "cfg.h" | 16 | #include "cfg.h" |
17 | #include "ieee80211_rate.h" | 17 | #include "rate.h" |
18 | #include "mesh.h" | ||
18 | 19 | ||
19 | static enum ieee80211_if_types | 20 | static enum ieee80211_if_types |
20 | nl80211_type_to_mac80211_type(enum nl80211_iftype type) | 21 | nl80211_type_to_mac80211_type(enum nl80211_iftype type) |
@@ -28,16 +29,26 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type) | |||
28 | return IEEE80211_IF_TYPE_STA; | 29 | return IEEE80211_IF_TYPE_STA; |
29 | case NL80211_IFTYPE_MONITOR: | 30 | case NL80211_IFTYPE_MONITOR: |
30 | return IEEE80211_IF_TYPE_MNTR; | 31 | return IEEE80211_IF_TYPE_MNTR; |
32 | #ifdef CONFIG_MAC80211_MESH | ||
33 | case NL80211_IFTYPE_MESH_POINT: | ||
34 | return IEEE80211_IF_TYPE_MESH_POINT; | ||
35 | #endif | ||
36 | case NL80211_IFTYPE_WDS: | ||
37 | return IEEE80211_IF_TYPE_WDS; | ||
31 | default: | 38 | default: |
32 | return IEEE80211_IF_TYPE_INVALID; | 39 | return IEEE80211_IF_TYPE_INVALID; |
33 | } | 40 | } |
34 | } | 41 | } |
35 | 42 | ||
36 | static int ieee80211_add_iface(struct wiphy *wiphy, char *name, | 43 | static int ieee80211_add_iface(struct wiphy *wiphy, char *name, |
37 | enum nl80211_iftype type) | 44 | enum nl80211_iftype type, u32 *flags, |
45 | struct vif_params *params) | ||
38 | { | 46 | { |
39 | struct ieee80211_local *local = wiphy_priv(wiphy); | 47 | struct ieee80211_local *local = wiphy_priv(wiphy); |
40 | enum ieee80211_if_types itype; | 48 | enum ieee80211_if_types itype; |
49 | struct net_device *dev; | ||
50 | struct ieee80211_sub_if_data *sdata; | ||
51 | int err; | ||
41 | 52 | ||
42 | if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) | 53 | if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED)) |
43 | return -ENODEV; | 54 | return -ENODEV; |
@@ -46,7 +57,13 @@ static int ieee80211_add_iface(struct wiphy *wiphy, char *name, | |||
46 | if (itype == IEEE80211_IF_TYPE_INVALID) | 57 | if (itype == IEEE80211_IF_TYPE_INVALID) |
47 | return -EINVAL; | 58 | return -EINVAL; |
48 | 59 | ||
49 | return ieee80211_if_add(local->mdev, name, NULL, itype); | 60 | err = ieee80211_if_add(local->mdev, name, &dev, itype, params); |
61 | if (err || itype != IEEE80211_IF_TYPE_MNTR || !flags) | ||
62 | return err; | ||
63 | |||
64 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
65 | sdata->u.mntr_flags = *flags; | ||
66 | return 0; | ||
50 | } | 67 | } |
51 | 68 | ||
52 | static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) | 69 | static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) |
@@ -69,7 +86,8 @@ static int ieee80211_del_iface(struct wiphy *wiphy, int ifindex) | |||
69 | } | 86 | } |
70 | 87 | ||
71 | static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, | 88 | static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, |
72 | enum nl80211_iftype type) | 89 | enum nl80211_iftype type, u32 *flags, |
90 | struct vif_params *params) | ||
73 | { | 91 | { |
74 | struct ieee80211_local *local = wiphy_priv(wiphy); | 92 | struct ieee80211_local *local = wiphy_priv(wiphy); |
75 | struct net_device *dev; | 93 | struct net_device *dev; |
@@ -99,6 +117,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex, | |||
99 | ieee80211_if_reinit(dev); | 117 | ieee80211_if_reinit(dev); |
100 | ieee80211_if_set_type(dev, itype); | 118 | ieee80211_if_set_type(dev, itype); |
101 | 119 | ||
120 | if (ieee80211_vif_is_mesh(&sdata->vif) && params->mesh_id_len) | ||
121 | ieee80211_if_sta_set_mesh_id(&sdata->u.sta, | ||
122 | params->mesh_id_len, | ||
123 | params->mesh_id); | ||
124 | |||
125 | if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags) | ||
126 | return 0; | ||
127 | |||
128 | sdata->u.mntr_flags = *flags; | ||
102 | return 0; | 129 | return 0; |
103 | } | 130 | } |
104 | 131 | ||
@@ -109,7 +136,8 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, | |||
109 | struct ieee80211_sub_if_data *sdata; | 136 | struct ieee80211_sub_if_data *sdata; |
110 | struct sta_info *sta = NULL; | 137 | struct sta_info *sta = NULL; |
111 | enum ieee80211_key_alg alg; | 138 | enum ieee80211_key_alg alg; |
112 | int ret; | 139 | struct ieee80211_key *key; |
140 | int err; | ||
113 | 141 | ||
114 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 142 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
115 | 143 | ||
@@ -128,21 +156,28 @@ static int ieee80211_add_key(struct wiphy *wiphy, struct net_device *dev, | |||
128 | return -EINVAL; | 156 | return -EINVAL; |
129 | } | 157 | } |
130 | 158 | ||
159 | key = ieee80211_key_alloc(alg, key_idx, params->key_len, params->key); | ||
160 | if (!key) | ||
161 | return -ENOMEM; | ||
162 | |||
163 | rcu_read_lock(); | ||
164 | |||
131 | if (mac_addr) { | 165 | if (mac_addr) { |
132 | sta = sta_info_get(sdata->local, mac_addr); | 166 | sta = sta_info_get(sdata->local, mac_addr); |
133 | if (!sta) | 167 | if (!sta) { |
134 | return -ENOENT; | 168 | ieee80211_key_free(key); |
169 | err = -ENOENT; | ||
170 | goto out_unlock; | ||
171 | } | ||
135 | } | 172 | } |
136 | 173 | ||
137 | ret = 0; | 174 | ieee80211_key_link(key, sdata, sta); |
138 | if (!ieee80211_key_alloc(sdata, sta, alg, key_idx, | ||
139 | params->key_len, params->key)) | ||
140 | ret = -ENOMEM; | ||
141 | 175 | ||
142 | if (sta) | 176 | err = 0; |
143 | sta_info_put(sta); | 177 | out_unlock: |
178 | rcu_read_unlock(); | ||
144 | 179 | ||
145 | return ret; | 180 | return err; |
146 | } | 181 | } |
147 | 182 | ||
148 | static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, | 183 | static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, |
@@ -154,27 +189,37 @@ static int ieee80211_del_key(struct wiphy *wiphy, struct net_device *dev, | |||
154 | 189 | ||
155 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 190 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
156 | 191 | ||
192 | rcu_read_lock(); | ||
193 | |||
157 | if (mac_addr) { | 194 | if (mac_addr) { |
195 | ret = -ENOENT; | ||
196 | |||
158 | sta = sta_info_get(sdata->local, mac_addr); | 197 | sta = sta_info_get(sdata->local, mac_addr); |
159 | if (!sta) | 198 | if (!sta) |
160 | return -ENOENT; | 199 | goto out_unlock; |
161 | 200 | ||
162 | ret = 0; | 201 | if (sta->key) { |
163 | if (sta->key) | ||
164 | ieee80211_key_free(sta->key); | 202 | ieee80211_key_free(sta->key); |
165 | else | 203 | WARN_ON(sta->key); |
166 | ret = -ENOENT; | 204 | ret = 0; |
205 | } | ||
167 | 206 | ||
168 | sta_info_put(sta); | 207 | goto out_unlock; |
169 | return ret; | ||
170 | } | 208 | } |
171 | 209 | ||
172 | if (!sdata->keys[key_idx]) | 210 | if (!sdata->keys[key_idx]) { |
173 | return -ENOENT; | 211 | ret = -ENOENT; |
212 | goto out_unlock; | ||
213 | } | ||
174 | 214 | ||
175 | ieee80211_key_free(sdata->keys[key_idx]); | 215 | ieee80211_key_free(sdata->keys[key_idx]); |
216 | WARN_ON(sdata->keys[key_idx]); | ||
176 | 217 | ||
177 | return 0; | 218 | ret = 0; |
219 | out_unlock: | ||
220 | rcu_read_unlock(); | ||
221 | |||
222 | return ret; | ||
178 | } | 223 | } |
179 | 224 | ||
180 | static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | 225 | static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, |
@@ -191,6 +236,8 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |||
191 | u16 iv16; | 236 | u16 iv16; |
192 | int err = -ENOENT; | 237 | int err = -ENOENT; |
193 | 238 | ||
239 | rcu_read_lock(); | ||
240 | |||
194 | if (mac_addr) { | 241 | if (mac_addr) { |
195 | sta = sta_info_get(sdata->local, mac_addr); | 242 | sta = sta_info_get(sdata->local, mac_addr); |
196 | if (!sta) | 243 | if (!sta) |
@@ -254,8 +301,7 @@ static int ieee80211_get_key(struct wiphy *wiphy, struct net_device *dev, | |||
254 | err = 0; | 301 | err = 0; |
255 | 302 | ||
256 | out: | 303 | out: |
257 | if (sta) | 304 | rcu_read_unlock(); |
258 | sta_info_put(sta); | ||
259 | return err; | 305 | return err; |
260 | } | 306 | } |
261 | 307 | ||
@@ -265,35 +311,83 @@ static int ieee80211_config_default_key(struct wiphy *wiphy, | |||
265 | { | 311 | { |
266 | struct ieee80211_sub_if_data *sdata; | 312 | struct ieee80211_sub_if_data *sdata; |
267 | 313 | ||
314 | rcu_read_lock(); | ||
315 | |||
268 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 316 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
269 | ieee80211_set_default_key(sdata, key_idx); | 317 | ieee80211_set_default_key(sdata, key_idx); |
270 | 318 | ||
319 | rcu_read_unlock(); | ||
320 | |||
271 | return 0; | 321 | return 0; |
272 | } | 322 | } |
273 | 323 | ||
324 | static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo) | ||
325 | { | ||
326 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
327 | |||
328 | sinfo->filled = STATION_INFO_INACTIVE_TIME | | ||
329 | STATION_INFO_RX_BYTES | | ||
330 | STATION_INFO_TX_BYTES; | ||
331 | |||
332 | sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); | ||
333 | sinfo->rx_bytes = sta->rx_bytes; | ||
334 | sinfo->tx_bytes = sta->tx_bytes; | ||
335 | |||
336 | if (ieee80211_vif_is_mesh(&sdata->vif)) { | ||
337 | #ifdef CONFIG_MAC80211_MESH | ||
338 | sinfo->filled |= STATION_INFO_LLID | | ||
339 | STATION_INFO_PLID | | ||
340 | STATION_INFO_PLINK_STATE; | ||
341 | |||
342 | sinfo->llid = le16_to_cpu(sta->llid); | ||
343 | sinfo->plid = le16_to_cpu(sta->plid); | ||
344 | sinfo->plink_state = sta->plink_state; | ||
345 | #endif | ||
346 | } | ||
347 | } | ||
348 | |||
349 | |||
350 | static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev, | ||
351 | int idx, u8 *mac, struct station_info *sinfo) | ||
352 | { | ||
353 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
354 | struct sta_info *sta; | ||
355 | int ret = -ENOENT; | ||
356 | |||
357 | rcu_read_lock(); | ||
358 | |||
359 | sta = sta_info_get_by_idx(local, idx, dev); | ||
360 | if (sta) { | ||
361 | ret = 0; | ||
362 | memcpy(mac, sta->addr, ETH_ALEN); | ||
363 | sta_set_sinfo(sta, sinfo); | ||
364 | } | ||
365 | |||
366 | rcu_read_unlock(); | ||
367 | |||
368 | return ret; | ||
369 | } | ||
370 | |||
274 | static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, | 371 | static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev, |
275 | u8 *mac, struct station_stats *stats) | 372 | u8 *mac, struct station_info *sinfo) |
276 | { | 373 | { |
277 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 374 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
278 | struct sta_info *sta; | 375 | struct sta_info *sta; |
376 | int ret = -ENOENT; | ||
279 | 377 | ||
280 | sta = sta_info_get(local, mac); | 378 | rcu_read_lock(); |
281 | if (!sta) | ||
282 | return -ENOENT; | ||
283 | 379 | ||
284 | /* XXX: verify sta->dev == dev */ | 380 | /* XXX: verify sta->dev == dev */ |
285 | 381 | ||
286 | stats->filled = STATION_STAT_INACTIVE_TIME | | 382 | sta = sta_info_get(local, mac); |
287 | STATION_STAT_RX_BYTES | | 383 | if (sta) { |
288 | STATION_STAT_TX_BYTES; | 384 | ret = 0; |
289 | 385 | sta_set_sinfo(sta, sinfo); | |
290 | stats->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx); | 386 | } |
291 | stats->rx_bytes = sta->rx_bytes; | ||
292 | stats->tx_bytes = sta->tx_bytes; | ||
293 | 387 | ||
294 | sta_info_put(sta); | 388 | rcu_read_unlock(); |
295 | 389 | ||
296 | return 0; | 390 | return ret; |
297 | } | 391 | } |
298 | 392 | ||
299 | /* | 393 | /* |
@@ -486,8 +580,8 @@ static void ieee80211_send_layer2_update(struct sta_info *sta) | |||
486 | msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ | 580 | msg->xid_info[1] = 1; /* LLC types/classes: Type 1 LLC */ |
487 | msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ | 581 | msg->xid_info[2] = 0; /* XID sender's receive window size (RW) */ |
488 | 582 | ||
489 | skb->dev = sta->dev; | 583 | skb->dev = sta->sdata->dev; |
490 | skb->protocol = eth_type_trans(skb, sta->dev); | 584 | skb->protocol = eth_type_trans(skb, sta->sdata->dev); |
491 | memset(skb->cb, 0, sizeof(skb->cb)); | 585 | memset(skb->cb, 0, sizeof(skb->cb)); |
492 | netif_rx(skb); | 586 | netif_rx(skb); |
493 | } | 587 | } |
@@ -498,7 +592,14 @@ static void sta_apply_parameters(struct ieee80211_local *local, | |||
498 | { | 592 | { |
499 | u32 rates; | 593 | u32 rates; |
500 | int i, j; | 594 | int i, j; |
501 | struct ieee80211_hw_mode *mode; | 595 | struct ieee80211_supported_band *sband; |
596 | struct ieee80211_sub_if_data *sdata = sta->sdata; | ||
597 | |||
598 | /* | ||
599 | * FIXME: updating the flags is racy when this function is | ||
600 | * called from ieee80211_change_station(), this will | ||
601 | * be resolved in a future patch. | ||
602 | */ | ||
502 | 603 | ||
503 | if (params->station_flags & STATION_FLAG_CHANGED) { | 604 | if (params->station_flags & STATION_FLAG_CHANGED) { |
504 | sta->flags &= ~WLAN_STA_AUTHORIZED; | 605 | sta->flags &= ~WLAN_STA_AUTHORIZED; |
@@ -514,6 +615,13 @@ static void sta_apply_parameters(struct ieee80211_local *local, | |||
514 | sta->flags |= WLAN_STA_WME; | 615 | sta->flags |= WLAN_STA_WME; |
515 | } | 616 | } |
516 | 617 | ||
618 | /* | ||
619 | * FIXME: updating the following information is racy when this | ||
620 | * function is called from ieee80211_change_station(). | ||
621 | * However, all this information should be static so | ||
622 | * maybe we should just reject attemps to change it. | ||
623 | */ | ||
624 | |||
517 | if (params->aid) { | 625 | if (params->aid) { |
518 | sta->aid = params->aid; | 626 | sta->aid = params->aid; |
519 | if (sta->aid > IEEE80211_MAX_AID) | 627 | if (sta->aid > IEEE80211_MAX_AID) |
@@ -525,15 +633,27 @@ static void sta_apply_parameters(struct ieee80211_local *local, | |||
525 | 633 | ||
526 | if (params->supported_rates) { | 634 | if (params->supported_rates) { |
527 | rates = 0; | 635 | rates = 0; |
528 | mode = local->oper_hw_mode; | 636 | sband = local->hw.wiphy->bands[local->oper_channel->band]; |
637 | |||
529 | for (i = 0; i < params->supported_rates_len; i++) { | 638 | for (i = 0; i < params->supported_rates_len; i++) { |
530 | int rate = (params->supported_rates[i] & 0x7f) * 5; | 639 | int rate = (params->supported_rates[i] & 0x7f) * 5; |
531 | for (j = 0; j < mode->num_rates; j++) { | 640 | for (j = 0; j < sband->n_bitrates; j++) { |
532 | if (mode->rates[j].rate == rate) | 641 | if (sband->bitrates[j].bitrate == rate) |
533 | rates |= BIT(j); | 642 | rates |= BIT(j); |
534 | } | 643 | } |
535 | } | 644 | } |
536 | sta->supp_rates = rates; | 645 | sta->supp_rates[local->oper_channel->band] = rates; |
646 | } | ||
647 | |||
648 | if (ieee80211_vif_is_mesh(&sdata->vif) && params->plink_action) { | ||
649 | switch (params->plink_action) { | ||
650 | case PLINK_ACTION_OPEN: | ||
651 | mesh_plink_open(sta); | ||
652 | break; | ||
653 | case PLINK_ACTION_BLOCK: | ||
654 | mesh_plink_block(sta); | ||
655 | break; | ||
656 | } | ||
537 | } | 657 | } |
538 | } | 658 | } |
539 | 659 | ||
@@ -543,18 +663,12 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |||
543 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 663 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); |
544 | struct sta_info *sta; | 664 | struct sta_info *sta; |
545 | struct ieee80211_sub_if_data *sdata; | 665 | struct ieee80211_sub_if_data *sdata; |
666 | int err; | ||
546 | 667 | ||
547 | /* Prevent a race with changing the rate control algorithm */ | 668 | /* Prevent a race with changing the rate control algorithm */ |
548 | if (!netif_running(dev)) | 669 | if (!netif_running(dev)) |
549 | return -ENETDOWN; | 670 | return -ENETDOWN; |
550 | 671 | ||
551 | /* XXX: get sta belonging to dev */ | ||
552 | sta = sta_info_get(local, mac); | ||
553 | if (sta) { | ||
554 | sta_info_put(sta); | ||
555 | return -EEXIST; | ||
556 | } | ||
557 | |||
558 | if (params->vlan) { | 672 | if (params->vlan) { |
559 | sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); | 673 | sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); |
560 | 674 | ||
@@ -564,22 +678,36 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |||
564 | } else | 678 | } else |
565 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); | 679 | sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
566 | 680 | ||
567 | sta = sta_info_add(local, dev, mac, GFP_KERNEL); | 681 | if (compare_ether_addr(mac, dev->dev_addr) == 0) |
682 | return -EINVAL; | ||
683 | |||
684 | if (is_multicast_ether_addr(mac)) | ||
685 | return -EINVAL; | ||
686 | |||
687 | sta = sta_info_alloc(sdata, mac, GFP_KERNEL); | ||
568 | if (!sta) | 688 | if (!sta) |
569 | return -ENOMEM; | 689 | return -ENOMEM; |
570 | 690 | ||
571 | sta->dev = sdata->dev; | ||
572 | if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN || | ||
573 | sdata->vif.type == IEEE80211_IF_TYPE_AP) | ||
574 | ieee80211_send_layer2_update(sta); | ||
575 | |||
576 | sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; | 691 | sta->flags = WLAN_STA_AUTH | WLAN_STA_ASSOC; |
577 | 692 | ||
578 | sta_apply_parameters(local, sta, params); | 693 | sta_apply_parameters(local, sta, params); |
579 | 694 | ||
580 | rate_control_rate_init(sta, local); | 695 | rate_control_rate_init(sta, local); |
581 | 696 | ||
582 | sta_info_put(sta); | 697 | rcu_read_lock(); |
698 | |||
699 | err = sta_info_insert(sta); | ||
700 | if (err) { | ||
701 | /* STA has been freed */ | ||
702 | rcu_read_unlock(); | ||
703 | return err; | ||
704 | } | ||
705 | |||
706 | if (sdata->vif.type == IEEE80211_IF_TYPE_VLAN || | ||
707 | sdata->vif.type == IEEE80211_IF_TYPE_AP) | ||
708 | ieee80211_send_layer2_update(sta); | ||
709 | |||
710 | rcu_read_unlock(); | ||
583 | 711 | ||
584 | return 0; | 712 | return 0; |
585 | } | 713 | } |
@@ -587,19 +715,26 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev, | |||
587 | static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, | 715 | static int ieee80211_del_station(struct wiphy *wiphy, struct net_device *dev, |
588 | u8 *mac) | 716 | u8 *mac) |
589 | { | 717 | { |
590 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | 718 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); |
719 | struct ieee80211_local *local = sdata->local; | ||
591 | struct sta_info *sta; | 720 | struct sta_info *sta; |
592 | 721 | ||
593 | if (mac) { | 722 | if (mac) { |
723 | rcu_read_lock(); | ||
724 | |||
594 | /* XXX: get sta belonging to dev */ | 725 | /* XXX: get sta belonging to dev */ |
595 | sta = sta_info_get(local, mac); | 726 | sta = sta_info_get(local, mac); |
596 | if (!sta) | 727 | if (!sta) { |
728 | rcu_read_unlock(); | ||
597 | return -ENOENT; | 729 | return -ENOENT; |
730 | } | ||
731 | |||
732 | sta_info_unlink(&sta); | ||
733 | rcu_read_unlock(); | ||
598 | 734 | ||
599 | sta_info_free(sta); | 735 | sta_info_destroy(sta); |
600 | sta_info_put(sta); | ||
601 | } else | 736 | } else |
602 | sta_info_flush(local, dev); | 737 | sta_info_flush(local, sdata); |
603 | 738 | ||
604 | return 0; | 739 | return 0; |
605 | } | 740 | } |
@@ -613,28 +748,203 @@ static int ieee80211_change_station(struct wiphy *wiphy, | |||
613 | struct sta_info *sta; | 748 | struct sta_info *sta; |
614 | struct ieee80211_sub_if_data *vlansdata; | 749 | struct ieee80211_sub_if_data *vlansdata; |
615 | 750 | ||
751 | rcu_read_lock(); | ||
752 | |||
616 | /* XXX: get sta belonging to dev */ | 753 | /* XXX: get sta belonging to dev */ |
617 | sta = sta_info_get(local, mac); | 754 | sta = sta_info_get(local, mac); |
618 | if (!sta) | 755 | if (!sta) { |
756 | rcu_read_unlock(); | ||
619 | return -ENOENT; | 757 | return -ENOENT; |
758 | } | ||
620 | 759 | ||
621 | if (params->vlan && params->vlan != sta->dev) { | 760 | if (params->vlan && params->vlan != sta->sdata->dev) { |
622 | vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); | 761 | vlansdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); |
623 | 762 | ||
624 | if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN || | 763 | if (vlansdata->vif.type != IEEE80211_IF_TYPE_VLAN || |
625 | vlansdata->vif.type != IEEE80211_IF_TYPE_AP) | 764 | vlansdata->vif.type != IEEE80211_IF_TYPE_AP) { |
765 | rcu_read_unlock(); | ||
626 | return -EINVAL; | 766 | return -EINVAL; |
767 | } | ||
627 | 768 | ||
628 | sta->dev = params->vlan; | 769 | sta->sdata = IEEE80211_DEV_TO_SUB_IF(params->vlan); |
629 | ieee80211_send_layer2_update(sta); | 770 | ieee80211_send_layer2_update(sta); |
630 | } | 771 | } |
631 | 772 | ||
632 | sta_apply_parameters(local, sta, params); | 773 | sta_apply_parameters(local, sta, params); |
633 | 774 | ||
634 | sta_info_put(sta); | 775 | rcu_read_unlock(); |
776 | |||
777 | return 0; | ||
778 | } | ||
779 | |||
780 | #ifdef CONFIG_MAC80211_MESH | ||
781 | static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev, | ||
782 | u8 *dst, u8 *next_hop) | ||
783 | { | ||
784 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
785 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
786 | struct mesh_path *mpath; | ||
787 | struct sta_info *sta; | ||
788 | int err; | ||
789 | |||
790 | if (!netif_running(dev)) | ||
791 | return -ENETDOWN; | ||
792 | |||
793 | if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) | ||
794 | return -ENOTSUPP; | ||
795 | |||
796 | rcu_read_lock(); | ||
797 | sta = sta_info_get(local, next_hop); | ||
798 | if (!sta) { | ||
799 | rcu_read_unlock(); | ||
800 | return -ENOENT; | ||
801 | } | ||
635 | 802 | ||
803 | err = mesh_path_add(dst, dev); | ||
804 | if (err) { | ||
805 | rcu_read_unlock(); | ||
806 | return err; | ||
807 | } | ||
808 | |||
809 | mpath = mesh_path_lookup(dst, dev); | ||
810 | if (!mpath) { | ||
811 | rcu_read_unlock(); | ||
812 | return -ENXIO; | ||
813 | } | ||
814 | mesh_path_fix_nexthop(mpath, sta); | ||
815 | |||
816 | rcu_read_unlock(); | ||
817 | return 0; | ||
818 | } | ||
819 | |||
820 | static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev, | ||
821 | u8 *dst) | ||
822 | { | ||
823 | if (dst) | ||
824 | return mesh_path_del(dst, dev); | ||
825 | |||
826 | mesh_path_flush(dev); | ||
827 | return 0; | ||
828 | } | ||
829 | |||
830 | static int ieee80211_change_mpath(struct wiphy *wiphy, | ||
831 | struct net_device *dev, | ||
832 | u8 *dst, u8 *next_hop) | ||
833 | { | ||
834 | struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr); | ||
835 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
836 | struct mesh_path *mpath; | ||
837 | struct sta_info *sta; | ||
838 | |||
839 | if (!netif_running(dev)) | ||
840 | return -ENETDOWN; | ||
841 | |||
842 | if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) | ||
843 | return -ENOTSUPP; | ||
844 | |||
845 | rcu_read_lock(); | ||
846 | |||
847 | sta = sta_info_get(local, next_hop); | ||
848 | if (!sta) { | ||
849 | rcu_read_unlock(); | ||
850 | return -ENOENT; | ||
851 | } | ||
852 | |||
853 | mpath = mesh_path_lookup(dst, dev); | ||
854 | if (!mpath) { | ||
855 | rcu_read_unlock(); | ||
856 | return -ENOENT; | ||
857 | } | ||
858 | |||
859 | mesh_path_fix_nexthop(mpath, sta); | ||
860 | |||
861 | rcu_read_unlock(); | ||
862 | return 0; | ||
863 | } | ||
864 | |||
865 | static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop, | ||
866 | struct mpath_info *pinfo) | ||
867 | { | ||
868 | if (mpath->next_hop) | ||
869 | memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN); | ||
870 | else | ||
871 | memset(next_hop, 0, ETH_ALEN); | ||
872 | |||
873 | pinfo->filled = MPATH_INFO_FRAME_QLEN | | ||
874 | MPATH_INFO_DSN | | ||
875 | MPATH_INFO_METRIC | | ||
876 | MPATH_INFO_EXPTIME | | ||
877 | MPATH_INFO_DISCOVERY_TIMEOUT | | ||
878 | MPATH_INFO_DISCOVERY_RETRIES | | ||
879 | MPATH_INFO_FLAGS; | ||
880 | |||
881 | pinfo->frame_qlen = mpath->frame_queue.qlen; | ||
882 | pinfo->dsn = mpath->dsn; | ||
883 | pinfo->metric = mpath->metric; | ||
884 | if (time_before(jiffies, mpath->exp_time)) | ||
885 | pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies); | ||
886 | pinfo->discovery_timeout = | ||
887 | jiffies_to_msecs(mpath->discovery_timeout); | ||
888 | pinfo->discovery_retries = mpath->discovery_retries; | ||
889 | pinfo->flags = 0; | ||
890 | if (mpath->flags & MESH_PATH_ACTIVE) | ||
891 | pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE; | ||
892 | if (mpath->flags & MESH_PATH_RESOLVING) | ||
893 | pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING; | ||
894 | if (mpath->flags & MESH_PATH_DSN_VALID) | ||
895 | pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID; | ||
896 | if (mpath->flags & MESH_PATH_FIXED) | ||
897 | pinfo->flags |= NL80211_MPATH_FLAG_FIXED; | ||
898 | if (mpath->flags & MESH_PATH_RESOLVING) | ||
899 | pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING; | ||
900 | |||
901 | pinfo->flags = mpath->flags; | ||
902 | } | ||
903 | |||
904 | static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev, | ||
905 | u8 *dst, u8 *next_hop, struct mpath_info *pinfo) | ||
906 | |||
907 | { | ||
908 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
909 | struct mesh_path *mpath; | ||
910 | |||
911 | if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) | ||
912 | return -ENOTSUPP; | ||
913 | |||
914 | rcu_read_lock(); | ||
915 | mpath = mesh_path_lookup(dst, dev); | ||
916 | if (!mpath) { | ||
917 | rcu_read_unlock(); | ||
918 | return -ENOENT; | ||
919 | } | ||
920 | memcpy(dst, mpath->dst, ETH_ALEN); | ||
921 | mpath_set_pinfo(mpath, next_hop, pinfo); | ||
922 | rcu_read_unlock(); | ||
923 | return 0; | ||
924 | } | ||
925 | |||
926 | static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev, | ||
927 | int idx, u8 *dst, u8 *next_hop, | ||
928 | struct mpath_info *pinfo) | ||
929 | { | ||
930 | struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev); | ||
931 | struct mesh_path *mpath; | ||
932 | |||
933 | if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT) | ||
934 | return -ENOTSUPP; | ||
935 | |||
936 | rcu_read_lock(); | ||
937 | mpath = mesh_path_lookup_by_idx(idx, dev); | ||
938 | if (!mpath) { | ||
939 | rcu_read_unlock(); | ||
940 | return -ENOENT; | ||
941 | } | ||
942 | memcpy(dst, mpath->dst, ETH_ALEN); | ||
943 | mpath_set_pinfo(mpath, next_hop, pinfo); | ||
944 | rcu_read_unlock(); | ||
636 | return 0; | 945 | return 0; |
637 | } | 946 | } |
947 | #endif | ||
638 | 948 | ||
639 | struct cfg80211_ops mac80211_config_ops = { | 949 | struct cfg80211_ops mac80211_config_ops = { |
640 | .add_virtual_intf = ieee80211_add_iface, | 950 | .add_virtual_intf = ieee80211_add_iface, |
@@ -651,4 +961,12 @@ struct cfg80211_ops mac80211_config_ops = { | |||
651 | .del_station = ieee80211_del_station, | 961 | .del_station = ieee80211_del_station, |
652 | .change_station = ieee80211_change_station, | 962 | .change_station = ieee80211_change_station, |
653 | .get_station = ieee80211_get_station, | 963 | .get_station = ieee80211_get_station, |
964 | .dump_station = ieee80211_dump_station, | ||
965 | #ifdef CONFIG_MAC80211_MESH | ||
966 | .add_mpath = ieee80211_add_mpath, | ||
967 | .del_mpath = ieee80211_del_mpath, | ||
968 | .change_mpath = ieee80211_change_mpath, | ||
969 | .get_mpath = ieee80211_get_mpath, | ||
970 | .dump_mpath = ieee80211_dump_mpath, | ||
971 | #endif | ||
654 | }; | 972 | }; |