diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 182 |
1 files changed, 170 insertions, 12 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index d2cfde659e76..16f86356ac97 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -116,6 +116,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
116 | .len = IEEE80211_MAX_SSID_LEN }, | 116 | .len = IEEE80211_MAX_SSID_LEN }, |
117 | [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, | 117 | [NL80211_ATTR_AUTH_TYPE] = { .type = NLA_U32 }, |
118 | [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, | 118 | [NL80211_ATTR_REASON_CODE] = { .type = NLA_U16 }, |
119 | [NL80211_ATTR_FREQ_FIXED] = { .type = NLA_FLAG }, | ||
119 | }; | 120 | }; |
120 | 121 | ||
121 | /* IE validation */ | 122 | /* IE validation */ |
@@ -322,6 +323,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
322 | CMD(assoc, ASSOCIATE); | 323 | CMD(assoc, ASSOCIATE); |
323 | CMD(deauth, DEAUTHENTICATE); | 324 | CMD(deauth, DEAUTHENTICATE); |
324 | CMD(disassoc, DISASSOCIATE); | 325 | CMD(disassoc, DISASSOCIATE); |
326 | CMD(join_ibss, JOIN_IBSS); | ||
325 | 327 | ||
326 | #undef CMD | 328 | #undef CMD |
327 | nla_nest_end(msg, nl_cmds); | 329 | nla_nest_end(msg, nl_cmds); |
@@ -668,7 +670,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
668 | struct cfg80211_registered_device *drv; | 670 | struct cfg80211_registered_device *drv; |
669 | struct vif_params params; | 671 | struct vif_params params; |
670 | int err, ifindex; | 672 | int err, ifindex; |
671 | enum nl80211_iftype type; | 673 | enum nl80211_iftype otype, ntype; |
672 | struct net_device *dev; | 674 | struct net_device *dev; |
673 | u32 _flags, *flags = NULL; | 675 | u32 _flags, *flags = NULL; |
674 | bool change = false; | 676 | bool change = false; |
@@ -682,30 +684,27 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
682 | goto unlock_rtnl; | 684 | goto unlock_rtnl; |
683 | 685 | ||
684 | ifindex = dev->ifindex; | 686 | ifindex = dev->ifindex; |
685 | type = dev->ieee80211_ptr->iftype; | 687 | otype = ntype = dev->ieee80211_ptr->iftype; |
686 | dev_put(dev); | 688 | dev_put(dev); |
687 | 689 | ||
688 | if (info->attrs[NL80211_ATTR_IFTYPE]) { | 690 | if (info->attrs[NL80211_ATTR_IFTYPE]) { |
689 | enum nl80211_iftype ntype; | ||
690 | |||
691 | ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); | 691 | ntype = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); |
692 | if (type != ntype) | 692 | if (otype != ntype) |
693 | change = true; | 693 | change = true; |
694 | type = ntype; | 694 | if (ntype > NL80211_IFTYPE_MAX) { |
695 | if (type > NL80211_IFTYPE_MAX) { | ||
696 | err = -EINVAL; | 695 | err = -EINVAL; |
697 | goto unlock; | 696 | goto unlock; |
698 | } | 697 | } |
699 | } | 698 | } |
700 | 699 | ||
701 | if (!drv->ops->change_virtual_intf || | 700 | if (!drv->ops->change_virtual_intf || |
702 | !(drv->wiphy.interface_modes & (1 << type))) { | 701 | !(drv->wiphy.interface_modes & (1 << ntype))) { |
703 | err = -EOPNOTSUPP; | 702 | err = -EOPNOTSUPP; |
704 | goto unlock; | 703 | goto unlock; |
705 | } | 704 | } |
706 | 705 | ||
707 | if (info->attrs[NL80211_ATTR_MESH_ID]) { | 706 | if (info->attrs[NL80211_ATTR_MESH_ID]) { |
708 | if (type != NL80211_IFTYPE_MESH_POINT) { | 707 | if (ntype != NL80211_IFTYPE_MESH_POINT) { |
709 | err = -EINVAL; | 708 | err = -EINVAL; |
710 | goto unlock; | 709 | goto unlock; |
711 | } | 710 | } |
@@ -715,7 +714,7 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
715 | } | 714 | } |
716 | 715 | ||
717 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | 716 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { |
718 | if (type != NL80211_IFTYPE_MONITOR) { | 717 | if (ntype != NL80211_IFTYPE_MONITOR) { |
719 | err = -EINVAL; | 718 | err = -EINVAL; |
720 | goto unlock; | 719 | goto unlock; |
721 | } | 720 | } |
@@ -730,12 +729,17 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
730 | 729 | ||
731 | if (change) | 730 | if (change) |
732 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, | 731 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, |
733 | type, flags, ¶ms); | 732 | ntype, flags, ¶ms); |
734 | else | 733 | else |
735 | err = 0; | 734 | err = 0; |
736 | 735 | ||
737 | dev = __dev_get_by_index(&init_net, ifindex); | 736 | dev = __dev_get_by_index(&init_net, ifindex); |
738 | WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); | 737 | WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != ntype)); |
738 | |||
739 | if (dev && !err && (ntype != otype)) { | ||
740 | if (otype == NL80211_IFTYPE_ADHOC) | ||
741 | cfg80211_clear_ibss(dev); | ||
742 | } | ||
739 | 743 | ||
740 | unlock: | 744 | unlock: |
741 | cfg80211_put_dev(drv); | 745 | cfg80211_put_dev(drv); |
@@ -3052,6 +3056,114 @@ unlock_rtnl: | |||
3052 | return err; | 3056 | return err; |
3053 | } | 3057 | } |
3054 | 3058 | ||
3059 | static int nl80211_join_ibss(struct sk_buff *skb, struct genl_info *info) | ||
3060 | { | ||
3061 | struct cfg80211_registered_device *drv; | ||
3062 | struct net_device *dev; | ||
3063 | struct cfg80211_ibss_params ibss; | ||
3064 | struct wiphy *wiphy; | ||
3065 | int err; | ||
3066 | |||
3067 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | ||
3068 | return -EINVAL; | ||
3069 | |||
3070 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
3071 | !info->attrs[NL80211_ATTR_SSID] || | ||
3072 | !nla_len(info->attrs[NL80211_ATTR_SSID])) | ||
3073 | return -EINVAL; | ||
3074 | |||
3075 | rtnl_lock(); | ||
3076 | |||
3077 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
3078 | if (err) | ||
3079 | goto unlock_rtnl; | ||
3080 | |||
3081 | if (!drv->ops->join_ibss) { | ||
3082 | err = -EOPNOTSUPP; | ||
3083 | goto out; | ||
3084 | } | ||
3085 | |||
3086 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) { | ||
3087 | err = -EOPNOTSUPP; | ||
3088 | goto out; | ||
3089 | } | ||
3090 | |||
3091 | if (!netif_running(dev)) { | ||
3092 | err = -ENETDOWN; | ||
3093 | goto out; | ||
3094 | } | ||
3095 | |||
3096 | wiphy = &drv->wiphy; | ||
3097 | memset(&ibss, 0, sizeof(ibss)); | ||
3098 | |||
3099 | if (info->attrs[NL80211_ATTR_MAC]) | ||
3100 | ibss.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
3101 | ibss.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); | ||
3102 | ibss.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); | ||
3103 | |||
3104 | if (info->attrs[NL80211_ATTR_IE]) { | ||
3105 | ibss.ie = nla_data(info->attrs[NL80211_ATTR_IE]); | ||
3106 | ibss.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
3107 | } | ||
3108 | |||
3109 | ibss.channel = ieee80211_get_channel(wiphy, | ||
3110 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); | ||
3111 | if (!ibss.channel || | ||
3112 | ibss.channel->flags & IEEE80211_CHAN_NO_IBSS || | ||
3113 | ibss.channel->flags & IEEE80211_CHAN_DISABLED) { | ||
3114 | err = -EINVAL; | ||
3115 | goto out; | ||
3116 | } | ||
3117 | |||
3118 | ibss.channel_fixed = !!info->attrs[NL80211_ATTR_FREQ_FIXED]; | ||
3119 | |||
3120 | err = cfg80211_join_ibss(drv, dev, &ibss); | ||
3121 | |||
3122 | out: | ||
3123 | cfg80211_put_dev(drv); | ||
3124 | dev_put(dev); | ||
3125 | unlock_rtnl: | ||
3126 | rtnl_unlock(); | ||
3127 | return err; | ||
3128 | } | ||
3129 | |||
3130 | static int nl80211_leave_ibss(struct sk_buff *skb, struct genl_info *info) | ||
3131 | { | ||
3132 | struct cfg80211_registered_device *drv; | ||
3133 | struct net_device *dev; | ||
3134 | int err; | ||
3135 | |||
3136 | rtnl_lock(); | ||
3137 | |||
3138 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
3139 | if (err) | ||
3140 | goto unlock_rtnl; | ||
3141 | |||
3142 | if (!drv->ops->leave_ibss) { | ||
3143 | err = -EOPNOTSUPP; | ||
3144 | goto out; | ||
3145 | } | ||
3146 | |||
3147 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_ADHOC) { | ||
3148 | err = -EOPNOTSUPP; | ||
3149 | goto out; | ||
3150 | } | ||
3151 | |||
3152 | if (!netif_running(dev)) { | ||
3153 | err = -ENETDOWN; | ||
3154 | goto out; | ||
3155 | } | ||
3156 | |||
3157 | err = cfg80211_leave_ibss(drv, dev); | ||
3158 | |||
3159 | out: | ||
3160 | cfg80211_put_dev(drv); | ||
3161 | dev_put(dev); | ||
3162 | unlock_rtnl: | ||
3163 | rtnl_unlock(); | ||
3164 | return err; | ||
3165 | } | ||
3166 | |||
3055 | static struct genl_ops nl80211_ops[] = { | 3167 | static struct genl_ops nl80211_ops[] = { |
3056 | { | 3168 | { |
3057 | .cmd = NL80211_CMD_GET_WIPHY, | 3169 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -3253,6 +3365,18 @@ static struct genl_ops nl80211_ops[] = { | |||
3253 | .policy = nl80211_policy, | 3365 | .policy = nl80211_policy, |
3254 | .flags = GENL_ADMIN_PERM, | 3366 | .flags = GENL_ADMIN_PERM, |
3255 | }, | 3367 | }, |
3368 | { | ||
3369 | .cmd = NL80211_CMD_JOIN_IBSS, | ||
3370 | .doit = nl80211_join_ibss, | ||
3371 | .policy = nl80211_policy, | ||
3372 | .flags = GENL_ADMIN_PERM, | ||
3373 | }, | ||
3374 | { | ||
3375 | .cmd = NL80211_CMD_LEAVE_IBSS, | ||
3376 | .doit = nl80211_leave_ibss, | ||
3377 | .policy = nl80211_policy, | ||
3378 | .flags = GENL_ADMIN_PERM, | ||
3379 | }, | ||
3256 | }; | 3380 | }; |
3257 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 3381 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
3258 | .name = "mlme", | 3382 | .name = "mlme", |
@@ -3466,6 +3590,40 @@ void nl80211_send_disassoc(struct cfg80211_registered_device *rdev, | |||
3466 | NL80211_CMD_DISASSOCIATE); | 3590 | NL80211_CMD_DISASSOCIATE); |
3467 | } | 3591 | } |
3468 | 3592 | ||
3593 | void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, | ||
3594 | struct net_device *netdev, const u8 *bssid, | ||
3595 | gfp_t gfp) | ||
3596 | { | ||
3597 | struct sk_buff *msg; | ||
3598 | void *hdr; | ||
3599 | |||
3600 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
3601 | if (!msg) | ||
3602 | return; | ||
3603 | |||
3604 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_JOIN_IBSS); | ||
3605 | if (!hdr) { | ||
3606 | nlmsg_free(msg); | ||
3607 | return; | ||
3608 | } | ||
3609 | |||
3610 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
3611 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
3612 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); | ||
3613 | |||
3614 | if (genlmsg_end(msg, hdr) < 0) { | ||
3615 | nlmsg_free(msg); | ||
3616 | return; | ||
3617 | } | ||
3618 | |||
3619 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
3620 | return; | ||
3621 | |||
3622 | nla_put_failure: | ||
3623 | genlmsg_cancel(msg, hdr); | ||
3624 | nlmsg_free(msg); | ||
3625 | } | ||
3626 | |||
3469 | void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, | 3627 | void nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, |
3470 | struct net_device *netdev, const u8 *addr, | 3628 | struct net_device *netdev, const u8 *addr, |
3471 | enum nl80211_key_type key_type, int key_id, | 3629 | enum nl80211_key_type key_type, int key_id, |