diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 737 |
1 files changed, 737 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 48b0d453e4e1..e3a214f63f91 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -61,6 +61,27 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
61 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 61 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
62 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 62 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, | 63 | [NL80211_ATTR_IFNAME] = { .type = NLA_NUL_STRING, .len = IFNAMSIZ-1 }, |
64 | |||
65 | [NL80211_ATTR_MAC] = { .type = NLA_BINARY, .len = ETH_ALEN }, | ||
66 | |||
67 | [NL80211_ATTR_KEY_DATA] = { .type = NLA_BINARY, | ||
68 | .len = WLAN_MAX_KEY_LEN }, | ||
69 | [NL80211_ATTR_KEY_IDX] = { .type = NLA_U8 }, | ||
70 | [NL80211_ATTR_KEY_CIPHER] = { .type = NLA_U32 }, | ||
71 | [NL80211_ATTR_KEY_DEFAULT] = { .type = NLA_FLAG }, | ||
72 | |||
73 | [NL80211_ATTR_BEACON_INTERVAL] = { .type = NLA_U32 }, | ||
74 | [NL80211_ATTR_DTIM_PERIOD] = { .type = NLA_U32 }, | ||
75 | [NL80211_ATTR_BEACON_HEAD] = { .type = NLA_BINARY, | ||
76 | .len = IEEE80211_MAX_DATA_LEN }, | ||
77 | [NL80211_ATTR_BEACON_TAIL] = { .type = NLA_BINARY, | ||
78 | .len = IEEE80211_MAX_DATA_LEN }, | ||
79 | [NL80211_ATTR_STA_AID] = { .type = NLA_U16 }, | ||
80 | [NL80211_ATTR_STA_FLAGS] = { .type = NLA_NESTED }, | ||
81 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, | ||
82 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, | ||
83 | .len = NL80211_MAX_SUPP_RATES }, | ||
84 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | ||
64 | }; | 85 | }; |
65 | 86 | ||
66 | /* message building helper */ | 87 | /* message building helper */ |
@@ -335,6 +356,655 @@ static int nl80211_del_interface(struct sk_buff *skb, struct genl_info *info) | |||
335 | return err; | 356 | return err; |
336 | } | 357 | } |
337 | 358 | ||
359 | struct get_key_cookie { | ||
360 | struct sk_buff *msg; | ||
361 | int error; | ||
362 | }; | ||
363 | |||
364 | static void get_key_callback(void *c, struct key_params *params) | ||
365 | { | ||
366 | struct get_key_cookie *cookie = c; | ||
367 | |||
368 | if (params->key) | ||
369 | NLA_PUT(cookie->msg, NL80211_ATTR_KEY_DATA, | ||
370 | params->key_len, params->key); | ||
371 | |||
372 | if (params->seq) | ||
373 | NLA_PUT(cookie->msg, NL80211_ATTR_KEY_SEQ, | ||
374 | params->seq_len, params->seq); | ||
375 | |||
376 | if (params->cipher) | ||
377 | NLA_PUT_U32(cookie->msg, NL80211_ATTR_KEY_CIPHER, | ||
378 | params->cipher); | ||
379 | |||
380 | return; | ||
381 | nla_put_failure: | ||
382 | cookie->error = 1; | ||
383 | } | ||
384 | |||
385 | static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info) | ||
386 | { | ||
387 | struct cfg80211_registered_device *drv; | ||
388 | int err; | ||
389 | struct net_device *dev; | ||
390 | u8 key_idx = 0; | ||
391 | u8 *mac_addr = NULL; | ||
392 | struct get_key_cookie cookie = { | ||
393 | .error = 0, | ||
394 | }; | ||
395 | void *hdr; | ||
396 | struct sk_buff *msg; | ||
397 | |||
398 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | ||
399 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | ||
400 | |||
401 | if (key_idx > 3) | ||
402 | return -EINVAL; | ||
403 | |||
404 | if (info->attrs[NL80211_ATTR_MAC]) | ||
405 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
406 | |||
407 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
408 | if (err) | ||
409 | return err; | ||
410 | |||
411 | if (!drv->ops->get_key) { | ||
412 | err = -EOPNOTSUPP; | ||
413 | goto out; | ||
414 | } | ||
415 | |||
416 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
417 | if (!msg) { | ||
418 | err = -ENOMEM; | ||
419 | goto out; | ||
420 | } | ||
421 | |||
422 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
423 | NL80211_CMD_NEW_KEY); | ||
424 | |||
425 | if (IS_ERR(hdr)) { | ||
426 | err = PTR_ERR(hdr); | ||
427 | goto out; | ||
428 | } | ||
429 | |||
430 | cookie.msg = msg; | ||
431 | |||
432 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
433 | NLA_PUT_U8(msg, NL80211_ATTR_KEY_IDX, key_idx); | ||
434 | if (mac_addr) | ||
435 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | ||
436 | |||
437 | rtnl_lock(); | ||
438 | err = drv->ops->get_key(&drv->wiphy, dev, key_idx, mac_addr, | ||
439 | &cookie, get_key_callback); | ||
440 | rtnl_unlock(); | ||
441 | |||
442 | if (err) | ||
443 | goto out; | ||
444 | |||
445 | if (cookie.error) | ||
446 | goto nla_put_failure; | ||
447 | |||
448 | genlmsg_end(msg, hdr); | ||
449 | err = genlmsg_unicast(msg, info->snd_pid); | ||
450 | goto out; | ||
451 | |||
452 | nla_put_failure: | ||
453 | err = -ENOBUFS; | ||
454 | nlmsg_free(msg); | ||
455 | out: | ||
456 | cfg80211_put_dev(drv); | ||
457 | dev_put(dev); | ||
458 | return err; | ||
459 | } | ||
460 | |||
461 | static int nl80211_set_key(struct sk_buff *skb, struct genl_info *info) | ||
462 | { | ||
463 | struct cfg80211_registered_device *drv; | ||
464 | int err; | ||
465 | struct net_device *dev; | ||
466 | u8 key_idx; | ||
467 | |||
468 | if (!info->attrs[NL80211_ATTR_KEY_IDX]) | ||
469 | return -EINVAL; | ||
470 | |||
471 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | ||
472 | |||
473 | if (key_idx > 3) | ||
474 | return -EINVAL; | ||
475 | |||
476 | /* currently only support setting default key */ | ||
477 | if (!info->attrs[NL80211_ATTR_KEY_DEFAULT]) | ||
478 | return -EINVAL; | ||
479 | |||
480 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
481 | if (err) | ||
482 | return err; | ||
483 | |||
484 | if (!drv->ops->set_default_key) { | ||
485 | err = -EOPNOTSUPP; | ||
486 | goto out; | ||
487 | } | ||
488 | |||
489 | rtnl_lock(); | ||
490 | err = drv->ops->set_default_key(&drv->wiphy, dev, key_idx); | ||
491 | rtnl_unlock(); | ||
492 | |||
493 | out: | ||
494 | cfg80211_put_dev(drv); | ||
495 | dev_put(dev); | ||
496 | return err; | ||
497 | } | ||
498 | |||
499 | static int nl80211_new_key(struct sk_buff *skb, struct genl_info *info) | ||
500 | { | ||
501 | struct cfg80211_registered_device *drv; | ||
502 | int err; | ||
503 | struct net_device *dev; | ||
504 | struct key_params params; | ||
505 | u8 key_idx = 0; | ||
506 | u8 *mac_addr = NULL; | ||
507 | |||
508 | memset(¶ms, 0, sizeof(params)); | ||
509 | |||
510 | if (!info->attrs[NL80211_ATTR_KEY_CIPHER]) | ||
511 | return -EINVAL; | ||
512 | |||
513 | if (info->attrs[NL80211_ATTR_KEY_DATA]) { | ||
514 | params.key = nla_data(info->attrs[NL80211_ATTR_KEY_DATA]); | ||
515 | params.key_len = nla_len(info->attrs[NL80211_ATTR_KEY_DATA]); | ||
516 | } | ||
517 | |||
518 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | ||
519 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | ||
520 | |||
521 | params.cipher = nla_get_u32(info->attrs[NL80211_ATTR_KEY_CIPHER]); | ||
522 | |||
523 | if (info->attrs[NL80211_ATTR_MAC]) | ||
524 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
525 | |||
526 | if (key_idx > 3) | ||
527 | return -EINVAL; | ||
528 | |||
529 | /* | ||
530 | * Disallow pairwise keys with non-zero index unless it's WEP | ||
531 | * (because current deployments use pairwise WEP keys with | ||
532 | * non-zero indizes but 802.11i clearly specifies to use zero) | ||
533 | */ | ||
534 | if (mac_addr && key_idx && | ||
535 | params.cipher != WLAN_CIPHER_SUITE_WEP40 && | ||
536 | params.cipher != WLAN_CIPHER_SUITE_WEP104) | ||
537 | return -EINVAL; | ||
538 | |||
539 | /* TODO: add definitions for the lengths to linux/ieee80211.h */ | ||
540 | switch (params.cipher) { | ||
541 | case WLAN_CIPHER_SUITE_WEP40: | ||
542 | if (params.key_len != 5) | ||
543 | return -EINVAL; | ||
544 | break; | ||
545 | case WLAN_CIPHER_SUITE_TKIP: | ||
546 | if (params.key_len != 32) | ||
547 | return -EINVAL; | ||
548 | break; | ||
549 | case WLAN_CIPHER_SUITE_CCMP: | ||
550 | if (params.key_len != 16) | ||
551 | return -EINVAL; | ||
552 | break; | ||
553 | case WLAN_CIPHER_SUITE_WEP104: | ||
554 | if (params.key_len != 13) | ||
555 | return -EINVAL; | ||
556 | break; | ||
557 | default: | ||
558 | return -EINVAL; | ||
559 | } | ||
560 | |||
561 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
562 | if (err) | ||
563 | return err; | ||
564 | |||
565 | if (!drv->ops->add_key) { | ||
566 | err = -EOPNOTSUPP; | ||
567 | goto out; | ||
568 | } | ||
569 | |||
570 | rtnl_lock(); | ||
571 | err = drv->ops->add_key(&drv->wiphy, dev, key_idx, mac_addr, ¶ms); | ||
572 | rtnl_unlock(); | ||
573 | |||
574 | out: | ||
575 | cfg80211_put_dev(drv); | ||
576 | dev_put(dev); | ||
577 | return err; | ||
578 | } | ||
579 | |||
580 | static int nl80211_del_key(struct sk_buff *skb, struct genl_info *info) | ||
581 | { | ||
582 | struct cfg80211_registered_device *drv; | ||
583 | int err; | ||
584 | struct net_device *dev; | ||
585 | u8 key_idx = 0; | ||
586 | u8 *mac_addr = NULL; | ||
587 | |||
588 | if (info->attrs[NL80211_ATTR_KEY_IDX]) | ||
589 | key_idx = nla_get_u8(info->attrs[NL80211_ATTR_KEY_IDX]); | ||
590 | |||
591 | if (key_idx > 3) | ||
592 | return -EINVAL; | ||
593 | |||
594 | if (info->attrs[NL80211_ATTR_MAC]) | ||
595 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
596 | |||
597 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
598 | if (err) | ||
599 | return err; | ||
600 | |||
601 | if (!drv->ops->del_key) { | ||
602 | err = -EOPNOTSUPP; | ||
603 | goto out; | ||
604 | } | ||
605 | |||
606 | rtnl_lock(); | ||
607 | err = drv->ops->del_key(&drv->wiphy, dev, key_idx, mac_addr); | ||
608 | rtnl_unlock(); | ||
609 | |||
610 | out: | ||
611 | cfg80211_put_dev(drv); | ||
612 | dev_put(dev); | ||
613 | return err; | ||
614 | } | ||
615 | |||
616 | static int nl80211_addset_beacon(struct sk_buff *skb, struct genl_info *info) | ||
617 | { | ||
618 | int (*call)(struct wiphy *wiphy, struct net_device *dev, | ||
619 | struct beacon_parameters *info); | ||
620 | struct cfg80211_registered_device *drv; | ||
621 | int err; | ||
622 | struct net_device *dev; | ||
623 | struct beacon_parameters params; | ||
624 | int haveinfo = 0; | ||
625 | |||
626 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
627 | if (err) | ||
628 | return err; | ||
629 | |||
630 | switch (info->genlhdr->cmd) { | ||
631 | case NL80211_CMD_NEW_BEACON: | ||
632 | /* these are required for NEW_BEACON */ | ||
633 | if (!info->attrs[NL80211_ATTR_BEACON_INTERVAL] || | ||
634 | !info->attrs[NL80211_ATTR_DTIM_PERIOD] || | ||
635 | !info->attrs[NL80211_ATTR_BEACON_HEAD]) { | ||
636 | err = -EINVAL; | ||
637 | goto out; | ||
638 | } | ||
639 | |||
640 | call = drv->ops->add_beacon; | ||
641 | break; | ||
642 | case NL80211_CMD_SET_BEACON: | ||
643 | call = drv->ops->set_beacon; | ||
644 | break; | ||
645 | default: | ||
646 | WARN_ON(1); | ||
647 | err = -EOPNOTSUPP; | ||
648 | goto out; | ||
649 | } | ||
650 | |||
651 | if (!call) { | ||
652 | err = -EOPNOTSUPP; | ||
653 | goto out; | ||
654 | } | ||
655 | |||
656 | memset(¶ms, 0, sizeof(params)); | ||
657 | |||
658 | if (info->attrs[NL80211_ATTR_BEACON_INTERVAL]) { | ||
659 | params.interval = | ||
660 | nla_get_u32(info->attrs[NL80211_ATTR_BEACON_INTERVAL]); | ||
661 | haveinfo = 1; | ||
662 | } | ||
663 | |||
664 | if (info->attrs[NL80211_ATTR_DTIM_PERIOD]) { | ||
665 | params.dtim_period = | ||
666 | nla_get_u32(info->attrs[NL80211_ATTR_DTIM_PERIOD]); | ||
667 | haveinfo = 1; | ||
668 | } | ||
669 | |||
670 | if (info->attrs[NL80211_ATTR_BEACON_HEAD]) { | ||
671 | params.head = nla_data(info->attrs[NL80211_ATTR_BEACON_HEAD]); | ||
672 | params.head_len = | ||
673 | nla_len(info->attrs[NL80211_ATTR_BEACON_HEAD]); | ||
674 | haveinfo = 1; | ||
675 | } | ||
676 | |||
677 | if (info->attrs[NL80211_ATTR_BEACON_TAIL]) { | ||
678 | params.tail = nla_data(info->attrs[NL80211_ATTR_BEACON_TAIL]); | ||
679 | params.tail_len = | ||
680 | nla_len(info->attrs[NL80211_ATTR_BEACON_TAIL]); | ||
681 | haveinfo = 1; | ||
682 | } | ||
683 | |||
684 | if (!haveinfo) { | ||
685 | err = -EINVAL; | ||
686 | goto out; | ||
687 | } | ||
688 | |||
689 | rtnl_lock(); | ||
690 | err = call(&drv->wiphy, dev, ¶ms); | ||
691 | rtnl_unlock(); | ||
692 | |||
693 | out: | ||
694 | cfg80211_put_dev(drv); | ||
695 | dev_put(dev); | ||
696 | return err; | ||
697 | } | ||
698 | |||
699 | static int nl80211_del_beacon(struct sk_buff *skb, struct genl_info *info) | ||
700 | { | ||
701 | struct cfg80211_registered_device *drv; | ||
702 | int err; | ||
703 | struct net_device *dev; | ||
704 | |||
705 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
706 | if (err) | ||
707 | return err; | ||
708 | |||
709 | if (!drv->ops->del_beacon) { | ||
710 | err = -EOPNOTSUPP; | ||
711 | goto out; | ||
712 | } | ||
713 | |||
714 | rtnl_lock(); | ||
715 | err = drv->ops->del_beacon(&drv->wiphy, dev); | ||
716 | rtnl_unlock(); | ||
717 | |||
718 | out: | ||
719 | cfg80211_put_dev(drv); | ||
720 | dev_put(dev); | ||
721 | return err; | ||
722 | } | ||
723 | |||
724 | static const struct nla_policy sta_flags_policy[NL80211_STA_FLAG_MAX + 1] = { | ||
725 | [NL80211_STA_FLAG_AUTHORIZED] = { .type = NLA_FLAG }, | ||
726 | [NL80211_STA_FLAG_SHORT_PREAMBLE] = { .type = NLA_FLAG }, | ||
727 | [NL80211_STA_FLAG_WME] = { .type = NLA_FLAG }, | ||
728 | }; | ||
729 | |||
730 | static int parse_station_flags(struct nlattr *nla, u32 *staflags) | ||
731 | { | ||
732 | struct nlattr *flags[NL80211_STA_FLAG_MAX + 1]; | ||
733 | int flag; | ||
734 | |||
735 | *staflags = 0; | ||
736 | |||
737 | if (!nla) | ||
738 | return 0; | ||
739 | |||
740 | if (nla_parse_nested(flags, NL80211_STA_FLAG_MAX, | ||
741 | nla, sta_flags_policy)) | ||
742 | return -EINVAL; | ||
743 | |||
744 | *staflags = STATION_FLAG_CHANGED; | ||
745 | |||
746 | for (flag = 1; flag <= NL80211_STA_FLAG_MAX; flag++) | ||
747 | if (flags[flag]) | ||
748 | *staflags |= (1<<flag); | ||
749 | |||
750 | return 0; | ||
751 | } | ||
752 | |||
753 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | ||
754 | int flags, struct net_device *dev, | ||
755 | u8 *mac_addr, struct station_stats *stats) | ||
756 | { | ||
757 | void *hdr; | ||
758 | struct nlattr *statsattr; | ||
759 | |||
760 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | ||
761 | if (!hdr) | ||
762 | return -1; | ||
763 | |||
764 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
765 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | ||
766 | |||
767 | statsattr = nla_nest_start(msg, NL80211_ATTR_STA_STATS); | ||
768 | if (!statsattr) | ||
769 | goto nla_put_failure; | ||
770 | if (stats->filled & STATION_STAT_INACTIVE_TIME) | ||
771 | NLA_PUT_U32(msg, NL80211_STA_STAT_INACTIVE_TIME, | ||
772 | stats->inactive_time); | ||
773 | if (stats->filled & STATION_STAT_RX_BYTES) | ||
774 | NLA_PUT_U32(msg, NL80211_STA_STAT_RX_BYTES, | ||
775 | stats->rx_bytes); | ||
776 | if (stats->filled & STATION_STAT_TX_BYTES) | ||
777 | NLA_PUT_U32(msg, NL80211_STA_STAT_TX_BYTES, | ||
778 | stats->tx_bytes); | ||
779 | |||
780 | nla_nest_end(msg, statsattr); | ||
781 | |||
782 | return genlmsg_end(msg, hdr); | ||
783 | |||
784 | nla_put_failure: | ||
785 | return genlmsg_cancel(msg, hdr); | ||
786 | } | ||
787 | |||
788 | |||
789 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | ||
790 | { | ||
791 | struct cfg80211_registered_device *drv; | ||
792 | int err; | ||
793 | struct net_device *dev; | ||
794 | struct station_stats stats; | ||
795 | struct sk_buff *msg; | ||
796 | u8 *mac_addr = NULL; | ||
797 | |||
798 | memset(&stats, 0, sizeof(stats)); | ||
799 | |||
800 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
801 | return -EINVAL; | ||
802 | |||
803 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
804 | |||
805 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
806 | if (err) | ||
807 | return err; | ||
808 | |||
809 | if (!drv->ops->get_station) { | ||
810 | err = -EOPNOTSUPP; | ||
811 | goto out; | ||
812 | } | ||
813 | |||
814 | rtnl_lock(); | ||
815 | err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &stats); | ||
816 | rtnl_unlock(); | ||
817 | |||
818 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
819 | if (!msg) | ||
820 | goto out; | ||
821 | |||
822 | if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, | ||
823 | dev, mac_addr, &stats) < 0) | ||
824 | goto out_free; | ||
825 | |||
826 | err = genlmsg_unicast(msg, info->snd_pid); | ||
827 | goto out; | ||
828 | |||
829 | out_free: | ||
830 | nlmsg_free(msg); | ||
831 | |||
832 | out: | ||
833 | cfg80211_put_dev(drv); | ||
834 | dev_put(dev); | ||
835 | return err; | ||
836 | } | ||
837 | |||
838 | /* | ||
839 | * Get vlan interface making sure it is on the right wiphy. | ||
840 | */ | ||
841 | static int get_vlan(struct nlattr *vlanattr, | ||
842 | struct cfg80211_registered_device *rdev, | ||
843 | struct net_device **vlan) | ||
844 | { | ||
845 | *vlan = NULL; | ||
846 | |||
847 | if (vlanattr) { | ||
848 | *vlan = dev_get_by_index(&init_net, nla_get_u32(vlanattr)); | ||
849 | if (!*vlan) | ||
850 | return -ENODEV; | ||
851 | if (!(*vlan)->ieee80211_ptr) | ||
852 | return -EINVAL; | ||
853 | if ((*vlan)->ieee80211_ptr->wiphy != &rdev->wiphy) | ||
854 | return -EINVAL; | ||
855 | } | ||
856 | return 0; | ||
857 | } | ||
858 | |||
859 | static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | ||
860 | { | ||
861 | struct cfg80211_registered_device *drv; | ||
862 | int err; | ||
863 | struct net_device *dev; | ||
864 | struct station_parameters params; | ||
865 | u8 *mac_addr = NULL; | ||
866 | |||
867 | memset(¶ms, 0, sizeof(params)); | ||
868 | |||
869 | params.listen_interval = -1; | ||
870 | |||
871 | if (info->attrs[NL80211_ATTR_STA_AID]) | ||
872 | return -EINVAL; | ||
873 | |||
874 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
875 | return -EINVAL; | ||
876 | |||
877 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
878 | |||
879 | if (info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) { | ||
880 | params.supported_rates = | ||
881 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
882 | params.supported_rates_len = | ||
883 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
884 | } | ||
885 | |||
886 | if (info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | ||
887 | params.listen_interval = | ||
888 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | ||
889 | |||
890 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], | ||
891 | ¶ms.station_flags)) | ||
892 | return -EINVAL; | ||
893 | |||
894 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
895 | if (err) | ||
896 | return err; | ||
897 | |||
898 | err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); | ||
899 | if (err) | ||
900 | goto out; | ||
901 | |||
902 | if (!drv->ops->change_station) { | ||
903 | err = -EOPNOTSUPP; | ||
904 | goto out; | ||
905 | } | ||
906 | |||
907 | rtnl_lock(); | ||
908 | err = drv->ops->change_station(&drv->wiphy, dev, mac_addr, ¶ms); | ||
909 | rtnl_unlock(); | ||
910 | |||
911 | out: | ||
912 | if (params.vlan) | ||
913 | dev_put(params.vlan); | ||
914 | cfg80211_put_dev(drv); | ||
915 | dev_put(dev); | ||
916 | return err; | ||
917 | } | ||
918 | |||
919 | static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | ||
920 | { | ||
921 | struct cfg80211_registered_device *drv; | ||
922 | int err; | ||
923 | struct net_device *dev; | ||
924 | struct station_parameters params; | ||
925 | u8 *mac_addr = NULL; | ||
926 | |||
927 | memset(¶ms, 0, sizeof(params)); | ||
928 | |||
929 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
930 | return -EINVAL; | ||
931 | |||
932 | if (!info->attrs[NL80211_ATTR_STA_AID]) | ||
933 | return -EINVAL; | ||
934 | |||
935 | if (!info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]) | ||
936 | return -EINVAL; | ||
937 | |||
938 | if (!info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]) | ||
939 | return -EINVAL; | ||
940 | |||
941 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
942 | params.supported_rates = | ||
943 | nla_data(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
944 | params.supported_rates_len = | ||
945 | nla_len(info->attrs[NL80211_ATTR_STA_SUPPORTED_RATES]); | ||
946 | params.listen_interval = | ||
947 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | ||
948 | params.listen_interval = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | ||
949 | |||
950 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], | ||
951 | ¶ms.station_flags)) | ||
952 | return -EINVAL; | ||
953 | |||
954 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
955 | if (err) | ||
956 | return err; | ||
957 | |||
958 | err = get_vlan(info->attrs[NL80211_ATTR_STA_VLAN], drv, ¶ms.vlan); | ||
959 | if (err) | ||
960 | goto out; | ||
961 | |||
962 | if (!drv->ops->add_station) { | ||
963 | err = -EOPNOTSUPP; | ||
964 | goto out; | ||
965 | } | ||
966 | |||
967 | rtnl_lock(); | ||
968 | err = drv->ops->add_station(&drv->wiphy, dev, mac_addr, ¶ms); | ||
969 | rtnl_unlock(); | ||
970 | |||
971 | out: | ||
972 | if (params.vlan) | ||
973 | dev_put(params.vlan); | ||
974 | cfg80211_put_dev(drv); | ||
975 | dev_put(dev); | ||
976 | return err; | ||
977 | } | ||
978 | |||
979 | static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | ||
980 | { | ||
981 | struct cfg80211_registered_device *drv; | ||
982 | int err; | ||
983 | struct net_device *dev; | ||
984 | u8 *mac_addr = NULL; | ||
985 | |||
986 | if (info->attrs[NL80211_ATTR_MAC]) | ||
987 | mac_addr = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
988 | |||
989 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
990 | if (err) | ||
991 | return err; | ||
992 | |||
993 | if (!drv->ops->del_station) { | ||
994 | err = -EOPNOTSUPP; | ||
995 | goto out; | ||
996 | } | ||
997 | |||
998 | rtnl_lock(); | ||
999 | err = drv->ops->del_station(&drv->wiphy, dev, mac_addr); | ||
1000 | rtnl_unlock(); | ||
1001 | |||
1002 | out: | ||
1003 | cfg80211_put_dev(drv); | ||
1004 | dev_put(dev); | ||
1005 | return err; | ||
1006 | } | ||
1007 | |||
338 | static struct genl_ops nl80211_ops[] = { | 1008 | static struct genl_ops nl80211_ops[] = { |
339 | { | 1009 | { |
340 | .cmd = NL80211_CMD_GET_WIPHY, | 1010 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -374,6 +1044,73 @@ static struct genl_ops nl80211_ops[] = { | |||
374 | .policy = nl80211_policy, | 1044 | .policy = nl80211_policy, |
375 | .flags = GENL_ADMIN_PERM, | 1045 | .flags = GENL_ADMIN_PERM, |
376 | }, | 1046 | }, |
1047 | { | ||
1048 | .cmd = NL80211_CMD_GET_KEY, | ||
1049 | .doit = nl80211_get_key, | ||
1050 | .policy = nl80211_policy, | ||
1051 | .flags = GENL_ADMIN_PERM, | ||
1052 | }, | ||
1053 | { | ||
1054 | .cmd = NL80211_CMD_SET_KEY, | ||
1055 | .doit = nl80211_set_key, | ||
1056 | .policy = nl80211_policy, | ||
1057 | .flags = GENL_ADMIN_PERM, | ||
1058 | }, | ||
1059 | { | ||
1060 | .cmd = NL80211_CMD_NEW_KEY, | ||
1061 | .doit = nl80211_new_key, | ||
1062 | .policy = nl80211_policy, | ||
1063 | .flags = GENL_ADMIN_PERM, | ||
1064 | }, | ||
1065 | { | ||
1066 | .cmd = NL80211_CMD_DEL_KEY, | ||
1067 | .doit = nl80211_del_key, | ||
1068 | .policy = nl80211_policy, | ||
1069 | .flags = GENL_ADMIN_PERM, | ||
1070 | }, | ||
1071 | { | ||
1072 | .cmd = NL80211_CMD_SET_BEACON, | ||
1073 | .policy = nl80211_policy, | ||
1074 | .flags = GENL_ADMIN_PERM, | ||
1075 | .doit = nl80211_addset_beacon, | ||
1076 | }, | ||
1077 | { | ||
1078 | .cmd = NL80211_CMD_NEW_BEACON, | ||
1079 | .policy = nl80211_policy, | ||
1080 | .flags = GENL_ADMIN_PERM, | ||
1081 | .doit = nl80211_addset_beacon, | ||
1082 | }, | ||
1083 | { | ||
1084 | .cmd = NL80211_CMD_DEL_BEACON, | ||
1085 | .policy = nl80211_policy, | ||
1086 | .flags = GENL_ADMIN_PERM, | ||
1087 | .doit = nl80211_del_beacon, | ||
1088 | }, | ||
1089 | { | ||
1090 | .cmd = NL80211_CMD_GET_STATION, | ||
1091 | .doit = nl80211_get_station, | ||
1092 | /* TODO: implement dumpit */ | ||
1093 | .policy = nl80211_policy, | ||
1094 | .flags = GENL_ADMIN_PERM, | ||
1095 | }, | ||
1096 | { | ||
1097 | .cmd = NL80211_CMD_SET_STATION, | ||
1098 | .doit = nl80211_set_station, | ||
1099 | .policy = nl80211_policy, | ||
1100 | .flags = GENL_ADMIN_PERM, | ||
1101 | }, | ||
1102 | { | ||
1103 | .cmd = NL80211_CMD_NEW_STATION, | ||
1104 | .doit = nl80211_new_station, | ||
1105 | .policy = nl80211_policy, | ||
1106 | .flags = GENL_ADMIN_PERM, | ||
1107 | }, | ||
1108 | { | ||
1109 | .cmd = NL80211_CMD_DEL_STATION, | ||
1110 | .doit = nl80211_del_station, | ||
1111 | .policy = nl80211_policy, | ||
1112 | .flags = GENL_ADMIN_PERM, | ||
1113 | }, | ||
377 | }; | 1114 | }; |
378 | 1115 | ||
379 | /* multicast groups */ | 1116 | /* multicast groups */ |