diff options
Diffstat (limited to 'net')
-rw-r--r-- | net/mac80211/cfg.c | 2 | ||||
-rw-r--r-- | net/wireless/Makefile | 2 | ||||
-rw-r--r-- | net/wireless/core.c | 16 | ||||
-rw-r--r-- | net/wireless/core.h | 7 | ||||
-rw-r--r-- | net/wireless/nl80211.c | 368 | ||||
-rw-r--r-- | net/wireless/nl80211.h | 13 | ||||
-rw-r--r-- | net/wireless/sme.c | 224 |
7 files changed, 620 insertions, 12 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c index c34c1a41019a..03de4024597a 100644 --- a/net/mac80211/cfg.c +++ b/net/mac80211/cfg.c | |||
@@ -1262,7 +1262,7 @@ static int ieee80211_assoc(struct wiphy *wiphy, struct net_device *dev, | |||
1262 | sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; | 1262 | sdata->u.mgd.flags &= ~IEEE80211_STA_MFP_ENABLED; |
1263 | } | 1263 | } |
1264 | 1264 | ||
1265 | if (req->control_port) | 1265 | if (req->crypto.control_port) |
1266 | sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; | 1266 | sdata->u.mgd.flags |= IEEE80211_STA_CONTROL_PORT; |
1267 | else | 1267 | else |
1268 | sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; | 1268 | sdata->u.mgd.flags &= ~IEEE80211_STA_CONTROL_PORT; |
diff --git a/net/wireless/Makefile b/net/wireless/Makefile index f78c4832a9ca..750c08e31b10 100644 --- a/net/wireless/Makefile +++ b/net/wireless/Makefile | |||
@@ -5,7 +5,7 @@ obj-$(CONFIG_LIB80211_CRYPT_WEP) += lib80211_crypt_wep.o | |||
5 | obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o | 5 | obj-$(CONFIG_LIB80211_CRYPT_CCMP) += lib80211_crypt_ccmp.o |
6 | obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o | 6 | obj-$(CONFIG_LIB80211_CRYPT_TKIP) += lib80211_crypt_tkip.o |
7 | 7 | ||
8 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o | 8 | cfg80211-y += core.o sysfs.o radiotap.o util.o reg.o scan.o nl80211.o mlme.o ibss.o sme.o |
9 | cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o | 9 | cfg80211-$(CONFIG_CFG80211_DEBUGFS) += debugfs.o |
10 | cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o | 10 | cfg80211-$(CONFIG_WIRELESS_EXT) += wext-compat.o |
11 | 11 | ||
diff --git a/net/wireless/core.c b/net/wireless/core.c index d41b7412b212..314e00f70e3b 100644 --- a/net/wireless/core.c +++ b/net/wireless/core.c | |||
@@ -546,6 +546,7 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
546 | "symlink to netdev!\n"); | 546 | "symlink to netdev!\n"); |
547 | } | 547 | } |
548 | wdev->netdev = dev; | 548 | wdev->netdev = dev; |
549 | wdev->sme_state = CFG80211_SME_IDLE; | ||
549 | #ifdef CONFIG_WIRELESS_EXT | 550 | #ifdef CONFIG_WIRELESS_EXT |
550 | wdev->wext.default_key = -1; | 551 | wdev->wext.default_key = -1; |
551 | wdev->wext.default_mgmt_key = -1; | 552 | wdev->wext.default_mgmt_key = -1; |
@@ -553,11 +554,20 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb, | |||
553 | mutex_unlock(&rdev->devlist_mtx); | 554 | mutex_unlock(&rdev->devlist_mtx); |
554 | break; | 555 | break; |
555 | case NETDEV_GOING_DOWN: | 556 | case NETDEV_GOING_DOWN: |
556 | if (wdev->iftype != NL80211_IFTYPE_ADHOC) | ||
557 | break; | ||
558 | if (!wdev->ssid_len) | 557 | if (!wdev->ssid_len) |
559 | break; | 558 | break; |
560 | cfg80211_leave_ibss(rdev, dev, true); | 559 | |
560 | switch (wdev->iftype) { | ||
561 | case NL80211_IFTYPE_ADHOC: | ||
562 | cfg80211_leave_ibss(rdev, dev, true); | ||
563 | break; | ||
564 | case NL80211_IFTYPE_STATION: | ||
565 | cfg80211_disconnect(rdev, dev, | ||
566 | WLAN_REASON_DEAUTH_LEAVING); | ||
567 | break; | ||
568 | default: | ||
569 | break; | ||
570 | } | ||
561 | break; | 571 | break; |
562 | case NETDEV_UP: | 572 | case NETDEV_UP: |
563 | #ifdef CONFIG_WIRELESS_EXT | 573 | #ifdef CONFIG_WIRELESS_EXT |
diff --git a/net/wireless/core.h b/net/wireless/core.h index bc084b68865c..f93f96f85d2b 100644 --- a/net/wireless/core.h +++ b/net/wireless/core.h | |||
@@ -174,6 +174,13 @@ void cfg80211_clear_ibss(struct net_device *dev, bool nowext); | |||
174 | int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, | 174 | int cfg80211_leave_ibss(struct cfg80211_registered_device *rdev, |
175 | struct net_device *dev, bool nowext); | 175 | struct net_device *dev, bool nowext); |
176 | 176 | ||
177 | /* SME */ | ||
178 | int cfg80211_connect(struct cfg80211_registered_device *rdev, | ||
179 | struct net_device *dev, | ||
180 | struct cfg80211_connect_params *connect); | ||
181 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | ||
182 | struct net_device *dev, u16 reason); | ||
183 | |||
177 | /* internal helpers */ | 184 | /* internal helpers */ |
178 | int cfg80211_validate_key_settings(struct key_params *params, int key_idx, | 185 | int cfg80211_validate_key_settings(struct key_params *params, int key_idx, |
179 | const u8 *mac_addr); | 186 | const u8 *mac_addr); |
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index bb8de268a6bf..89dd3793e03c 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -128,6 +128,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
128 | .len = sizeof(struct nl80211_sta_flag_update), | 128 | .len = sizeof(struct nl80211_sta_flag_update), |
129 | }, | 129 | }, |
130 | [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, | 130 | [NL80211_ATTR_CONTROL_PORT] = { .type = NLA_FLAG }, |
131 | [NL80211_ATTR_PRIVACY] = { .type = NLA_FLAG }, | ||
132 | [NL80211_ATTR_CIPHER_SUITE_GROUP] = { .type = NLA_U32 }, | ||
133 | [NL80211_ATTR_WPA_VERSIONS] = { .type = NLA_U32 }, | ||
131 | }; | 134 | }; |
132 | 135 | ||
133 | /* IE validation */ | 136 | /* IE validation */ |
@@ -347,6 +350,17 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
347 | CMD(join_ibss, JOIN_IBSS); | 350 | CMD(join_ibss, JOIN_IBSS); |
348 | 351 | ||
349 | #undef CMD | 352 | #undef CMD |
353 | |||
354 | if (dev->ops->connect) { | ||
355 | i++; | ||
356 | NLA_PUT_U32(msg, i, NL80211_CMD_CONNECT); | ||
357 | } | ||
358 | |||
359 | if (dev->ops->disconnect) { | ||
360 | i++; | ||
361 | NLA_PUT_U32(msg, i, NL80211_CMD_DISCONNECT); | ||
362 | } | ||
363 | |||
350 | nla_nest_end(msg, nl_cmds); | 364 | nla_nest_end(msg, nl_cmds); |
351 | 365 | ||
352 | return genlmsg_end(msg, hdr); | 366 | return genlmsg_end(msg, hdr); |
@@ -3001,12 +3015,31 @@ static int nl80211_dump_scan(struct sk_buff *skb, | |||
3001 | 3015 | ||
3002 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) | 3016 | static bool nl80211_valid_auth_type(enum nl80211_auth_type auth_type) |
3003 | { | 3017 | { |
3004 | return auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM || | 3018 | return auth_type <= NL80211_AUTHTYPE_MAX; |
3005 | auth_type == NL80211_AUTHTYPE_SHARED_KEY || | 3019 | } |
3006 | auth_type == NL80211_AUTHTYPE_FT || | 3020 | |
3007 | auth_type == NL80211_AUTHTYPE_NETWORK_EAP; | 3021 | static bool nl80211_valid_wpa_versions(u32 wpa_versions) |
3022 | { | ||
3023 | return !(wpa_versions & ~(NL80211_WPA_VERSION_1 | | ||
3024 | NL80211_WPA_VERSION_2)); | ||
3025 | } | ||
3026 | |||
3027 | static bool nl80211_valid_akm_suite(u32 akm) | ||
3028 | { | ||
3029 | return akm == WLAN_AKM_SUITE_8021X || | ||
3030 | akm == WLAN_AKM_SUITE_PSK; | ||
3031 | } | ||
3032 | |||
3033 | static bool nl80211_valid_cipher_suite(u32 cipher) | ||
3034 | { | ||
3035 | return cipher == WLAN_CIPHER_SUITE_WEP40 || | ||
3036 | cipher == WLAN_CIPHER_SUITE_WEP104 || | ||
3037 | cipher == WLAN_CIPHER_SUITE_TKIP || | ||
3038 | cipher == WLAN_CIPHER_SUITE_CCMP || | ||
3039 | cipher == WLAN_CIPHER_SUITE_AES_CMAC; | ||
3008 | } | 3040 | } |
3009 | 3041 | ||
3042 | |||
3010 | static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) | 3043 | static int nl80211_authenticate(struct sk_buff *skb, struct genl_info *info) |
3011 | { | 3044 | { |
3012 | struct cfg80211_registered_device *drv; | 3045 | struct cfg80211_registered_device *drv; |
@@ -3086,6 +3119,68 @@ unlock_rtnl: | |||
3086 | return err; | 3119 | return err; |
3087 | } | 3120 | } |
3088 | 3121 | ||
3122 | static int nl80211_crypto_settings(struct genl_info *info, | ||
3123 | struct cfg80211_crypto_settings *settings) | ||
3124 | { | ||
3125 | settings->control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; | ||
3126 | |||
3127 | if (info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]) { | ||
3128 | void *data; | ||
3129 | int len, i; | ||
3130 | |||
3131 | data = nla_data(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); | ||
3132 | len = nla_len(info->attrs[NL80211_ATTR_CIPHER_SUITES_PAIRWISE]); | ||
3133 | settings->n_ciphers_pairwise = len / sizeof(u32); | ||
3134 | |||
3135 | if (len % sizeof(u32)) | ||
3136 | return -EINVAL; | ||
3137 | |||
3138 | if (settings->n_ciphers_pairwise > NL80211_MAX_NR_CIPHER_SUITES) | ||
3139 | return -EINVAL; | ||
3140 | |||
3141 | memcpy(settings->ciphers_pairwise, data, len); | ||
3142 | |||
3143 | for (i = 0; i < settings->n_ciphers_pairwise; i++) | ||
3144 | if (!nl80211_valid_cipher_suite( | ||
3145 | settings->ciphers_pairwise[i])) | ||
3146 | return -EINVAL; | ||
3147 | } | ||
3148 | |||
3149 | if (info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]) { | ||
3150 | settings->cipher_group = | ||
3151 | nla_get_u32(info->attrs[NL80211_ATTR_CIPHER_SUITE_GROUP]); | ||
3152 | if (!nl80211_valid_cipher_suite(settings->cipher_group)) | ||
3153 | return -EINVAL; | ||
3154 | } | ||
3155 | |||
3156 | if (info->attrs[NL80211_ATTR_WPA_VERSIONS]) { | ||
3157 | settings->wpa_versions = | ||
3158 | nla_get_u32(info->attrs[NL80211_ATTR_WPA_VERSIONS]); | ||
3159 | if (!nl80211_valid_wpa_versions(settings->wpa_versions)) | ||
3160 | return -EINVAL; | ||
3161 | } | ||
3162 | |||
3163 | if (info->attrs[NL80211_ATTR_AKM_SUITES]) { | ||
3164 | void *data; | ||
3165 | int len, i; | ||
3166 | |||
3167 | data = nla_data(info->attrs[NL80211_ATTR_AKM_SUITES]); | ||
3168 | len = nla_len(info->attrs[NL80211_ATTR_AKM_SUITES]); | ||
3169 | settings->n_akm_suites = len / sizeof(u32); | ||
3170 | |||
3171 | if (len % sizeof(u32)) | ||
3172 | return -EINVAL; | ||
3173 | |||
3174 | memcpy(settings->akm_suites, data, len); | ||
3175 | |||
3176 | for (i = 0; i < settings->n_ciphers_pairwise; i++) | ||
3177 | if (!nl80211_valid_akm_suite(settings->akm_suites[i])) | ||
3178 | return -EINVAL; | ||
3179 | } | ||
3180 | |||
3181 | return 0; | ||
3182 | } | ||
3183 | |||
3089 | static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | 3184 | static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) |
3090 | { | 3185 | { |
3091 | struct cfg80211_registered_device *drv; | 3186 | struct cfg80211_registered_device *drv; |
@@ -3156,9 +3251,9 @@ static int nl80211_associate(struct sk_buff *skb, struct genl_info *info) | |||
3156 | } | 3251 | } |
3157 | } | 3252 | } |
3158 | 3253 | ||
3159 | req.control_port = info->attrs[NL80211_ATTR_CONTROL_PORT]; | 3254 | err = nl80211_crypto_settings(info, &req.crypto); |
3160 | 3255 | if (!err) | |
3161 | err = drv->ops->assoc(&drv->wiphy, dev, &req); | 3256 | err = drv->ops->assoc(&drv->wiphy, dev, &req); |
3162 | 3257 | ||
3163 | out: | 3258 | out: |
3164 | cfg80211_put_dev(drv); | 3259 | cfg80211_put_dev(drv); |
@@ -3538,6 +3633,130 @@ void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) | |||
3538 | EXPORT_SYMBOL(cfg80211_testmode_event); | 3633 | EXPORT_SYMBOL(cfg80211_testmode_event); |
3539 | #endif | 3634 | #endif |
3540 | 3635 | ||
3636 | static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | ||
3637 | { | ||
3638 | struct cfg80211_registered_device *drv; | ||
3639 | struct net_device *dev; | ||
3640 | struct cfg80211_connect_params connect; | ||
3641 | struct wiphy *wiphy; | ||
3642 | int err; | ||
3643 | |||
3644 | memset(&connect, 0, sizeof(connect)); | ||
3645 | |||
3646 | if (!is_valid_ie_attr(info->attrs[NL80211_ATTR_IE])) | ||
3647 | return -EINVAL; | ||
3648 | |||
3649 | if (!info->attrs[NL80211_ATTR_SSID] || | ||
3650 | !nla_len(info->attrs[NL80211_ATTR_SSID])) | ||
3651 | return -EINVAL; | ||
3652 | |||
3653 | if (info->attrs[NL80211_ATTR_AUTH_TYPE]) { | ||
3654 | connect.auth_type = | ||
3655 | nla_get_u32(info->attrs[NL80211_ATTR_AUTH_TYPE]); | ||
3656 | if (!nl80211_valid_auth_type(connect.auth_type)) | ||
3657 | return -EINVAL; | ||
3658 | } else | ||
3659 | connect.auth_type = NL80211_AUTHTYPE_AUTOMATIC; | ||
3660 | |||
3661 | connect.privacy = info->attrs[NL80211_ATTR_PRIVACY]; | ||
3662 | |||
3663 | err = nl80211_crypto_settings(info, &connect.crypto); | ||
3664 | if (err) | ||
3665 | return err; | ||
3666 | rtnl_lock(); | ||
3667 | |||
3668 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
3669 | if (err) | ||
3670 | goto unlock_rtnl; | ||
3671 | |||
3672 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
3673 | err = -EOPNOTSUPP; | ||
3674 | goto out; | ||
3675 | } | ||
3676 | |||
3677 | if (!netif_running(dev)) { | ||
3678 | err = -ENETDOWN; | ||
3679 | goto out; | ||
3680 | } | ||
3681 | |||
3682 | wiphy = &drv->wiphy; | ||
3683 | |||
3684 | connect.bssid = NULL; | ||
3685 | connect.channel = NULL; | ||
3686 | connect.auth_type = NL80211_AUTHTYPE_OPEN_SYSTEM; | ||
3687 | |||
3688 | if (info->attrs[NL80211_ATTR_MAC]) | ||
3689 | connect.bssid = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
3690 | connect.ssid = nla_data(info->attrs[NL80211_ATTR_SSID]); | ||
3691 | connect.ssid_len = nla_len(info->attrs[NL80211_ATTR_SSID]); | ||
3692 | |||
3693 | if (info->attrs[NL80211_ATTR_IE]) { | ||
3694 | connect.ie = nla_data(info->attrs[NL80211_ATTR_IE]); | ||
3695 | connect.ie_len = nla_len(info->attrs[NL80211_ATTR_IE]); | ||
3696 | } | ||
3697 | |||
3698 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | ||
3699 | connect.channel = | ||
3700 | ieee80211_get_channel(wiphy, | ||
3701 | nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ])); | ||
3702 | if (!connect.channel || | ||
3703 | connect.channel->flags & IEEE80211_CHAN_DISABLED) { | ||
3704 | err = -EINVAL; | ||
3705 | goto out; | ||
3706 | } | ||
3707 | } | ||
3708 | |||
3709 | err = cfg80211_connect(drv, dev, &connect); | ||
3710 | |||
3711 | out: | ||
3712 | cfg80211_put_dev(drv); | ||
3713 | dev_put(dev); | ||
3714 | unlock_rtnl: | ||
3715 | rtnl_unlock(); | ||
3716 | return err; | ||
3717 | } | ||
3718 | |||
3719 | static int nl80211_disconnect(struct sk_buff *skb, struct genl_info *info) | ||
3720 | { | ||
3721 | struct cfg80211_registered_device *drv; | ||
3722 | struct net_device *dev; | ||
3723 | int err; | ||
3724 | u16 reason; | ||
3725 | |||
3726 | if (!info->attrs[NL80211_ATTR_REASON_CODE]) | ||
3727 | reason = WLAN_REASON_DEAUTH_LEAVING; | ||
3728 | else | ||
3729 | reason = nla_get_u16(info->attrs[NL80211_ATTR_REASON_CODE]); | ||
3730 | |||
3731 | if (reason == 0) | ||
3732 | return -EINVAL; | ||
3733 | |||
3734 | rtnl_lock(); | ||
3735 | |||
3736 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
3737 | if (err) | ||
3738 | goto unlock_rtnl; | ||
3739 | |||
3740 | if (dev->ieee80211_ptr->iftype != NL80211_IFTYPE_STATION) { | ||
3741 | err = -EOPNOTSUPP; | ||
3742 | goto out; | ||
3743 | } | ||
3744 | |||
3745 | if (!netif_running(dev)) { | ||
3746 | err = -ENETDOWN; | ||
3747 | goto out; | ||
3748 | } | ||
3749 | |||
3750 | err = cfg80211_disconnect(drv, dev, reason); | ||
3751 | |||
3752 | out: | ||
3753 | cfg80211_put_dev(drv); | ||
3754 | dev_put(dev); | ||
3755 | unlock_rtnl: | ||
3756 | rtnl_unlock(); | ||
3757 | return err; | ||
3758 | } | ||
3759 | |||
3541 | static struct genl_ops nl80211_ops[] = { | 3760 | static struct genl_ops nl80211_ops[] = { |
3542 | { | 3761 | { |
3543 | .cmd = NL80211_CMD_GET_WIPHY, | 3762 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -3759,6 +3978,18 @@ static struct genl_ops nl80211_ops[] = { | |||
3759 | .flags = GENL_ADMIN_PERM, | 3978 | .flags = GENL_ADMIN_PERM, |
3760 | }, | 3979 | }, |
3761 | #endif | 3980 | #endif |
3981 | { | ||
3982 | .cmd = NL80211_CMD_CONNECT, | ||
3983 | .doit = nl80211_connect, | ||
3984 | .policy = nl80211_policy, | ||
3985 | .flags = GENL_ADMIN_PERM, | ||
3986 | }, | ||
3987 | { | ||
3988 | .cmd = NL80211_CMD_DISCONNECT, | ||
3989 | .doit = nl80211_disconnect, | ||
3990 | .policy = nl80211_policy, | ||
3991 | .flags = GENL_ADMIN_PERM, | ||
3992 | }, | ||
3762 | }; | 3993 | }; |
3763 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 3994 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
3764 | .name = "mlme", | 3995 | .name = "mlme", |
@@ -4077,6 +4308,129 @@ void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, | |||
4077 | addr, gfp); | 4308 | addr, gfp); |
4078 | } | 4309 | } |
4079 | 4310 | ||
4311 | void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, | ||
4312 | struct net_device *netdev, const u8 *bssid, | ||
4313 | const u8 *req_ie, size_t req_ie_len, | ||
4314 | const u8 *resp_ie, size_t resp_ie_len, | ||
4315 | u16 status, gfp_t gfp) | ||
4316 | { | ||
4317 | struct sk_buff *msg; | ||
4318 | void *hdr; | ||
4319 | |||
4320 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
4321 | if (!msg) | ||
4322 | return; | ||
4323 | |||
4324 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_CONNECT); | ||
4325 | if (!hdr) { | ||
4326 | nlmsg_free(msg); | ||
4327 | return; | ||
4328 | } | ||
4329 | |||
4330 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
4331 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
4332 | if (bssid) | ||
4333 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); | ||
4334 | NLA_PUT_U16(msg, NL80211_ATTR_STATUS_CODE, status); | ||
4335 | if (req_ie) | ||
4336 | NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); | ||
4337 | if (resp_ie) | ||
4338 | NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); | ||
4339 | |||
4340 | if (genlmsg_end(msg, hdr) < 0) { | ||
4341 | nlmsg_free(msg); | ||
4342 | return; | ||
4343 | } | ||
4344 | |||
4345 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
4346 | return; | ||
4347 | |||
4348 | nla_put_failure: | ||
4349 | genlmsg_cancel(msg, hdr); | ||
4350 | nlmsg_free(msg); | ||
4351 | |||
4352 | } | ||
4353 | |||
4354 | void nl80211_send_roamed(struct cfg80211_registered_device *rdev, | ||
4355 | struct net_device *netdev, const u8 *bssid, | ||
4356 | const u8 *req_ie, size_t req_ie_len, | ||
4357 | const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) | ||
4358 | { | ||
4359 | struct sk_buff *msg; | ||
4360 | void *hdr; | ||
4361 | |||
4362 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
4363 | if (!msg) | ||
4364 | return; | ||
4365 | |||
4366 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_ROAM); | ||
4367 | if (!hdr) { | ||
4368 | nlmsg_free(msg); | ||
4369 | return; | ||
4370 | } | ||
4371 | |||
4372 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
4373 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
4374 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, bssid); | ||
4375 | if (req_ie) | ||
4376 | NLA_PUT(msg, NL80211_ATTR_REQ_IE, req_ie_len, req_ie); | ||
4377 | if (resp_ie) | ||
4378 | NLA_PUT(msg, NL80211_ATTR_RESP_IE, resp_ie_len, resp_ie); | ||
4379 | |||
4380 | if (genlmsg_end(msg, hdr) < 0) { | ||
4381 | nlmsg_free(msg); | ||
4382 | return; | ||
4383 | } | ||
4384 | |||
4385 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
4386 | return; | ||
4387 | |||
4388 | nla_put_failure: | ||
4389 | genlmsg_cancel(msg, hdr); | ||
4390 | nlmsg_free(msg); | ||
4391 | |||
4392 | } | ||
4393 | |||
4394 | void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, | ||
4395 | struct net_device *netdev, u16 reason, | ||
4396 | u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp) | ||
4397 | { | ||
4398 | struct sk_buff *msg; | ||
4399 | void *hdr; | ||
4400 | |||
4401 | msg = nlmsg_new(NLMSG_GOODSIZE, gfp); | ||
4402 | if (!msg) | ||
4403 | return; | ||
4404 | |||
4405 | hdr = nl80211hdr_put(msg, 0, 0, 0, NL80211_CMD_DISCONNECT); | ||
4406 | if (!hdr) { | ||
4407 | nlmsg_free(msg); | ||
4408 | return; | ||
4409 | } | ||
4410 | |||
4411 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
4412 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
4413 | if (from_ap && reason) | ||
4414 | NLA_PUT_U16(msg, NL80211_ATTR_REASON_CODE, reason); | ||
4415 | if (from_ap) | ||
4416 | NLA_PUT_FLAG(msg, NL80211_ATTR_DISCONNECTED_BY_AP); | ||
4417 | if (ie) | ||
4418 | NLA_PUT(msg, NL80211_ATTR_IE, ie_len, ie); | ||
4419 | |||
4420 | if (genlmsg_end(msg, hdr) < 0) { | ||
4421 | nlmsg_free(msg); | ||
4422 | return; | ||
4423 | } | ||
4424 | |||
4425 | genlmsg_multicast(msg, 0, nl80211_mlme_mcgrp.id, gfp); | ||
4426 | return; | ||
4427 | |||
4428 | nla_put_failure: | ||
4429 | genlmsg_cancel(msg, hdr); | ||
4430 | nlmsg_free(msg); | ||
4431 | |||
4432 | } | ||
4433 | |||
4080 | void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, | 4434 | void nl80211_send_ibss_bssid(struct cfg80211_registered_device *rdev, |
4081 | struct net_device *netdev, const u8 *bssid, | 4435 | struct net_device *netdev, const u8 *bssid, |
4082 | gfp_t gfp) | 4436 | gfp_t gfp) |
diff --git a/net/wireless/nl80211.h b/net/wireless/nl80211.h index 662c216e8d4f..cf3708b48c29 100644 --- a/net/wireless/nl80211.h +++ b/net/wireless/nl80211.h | |||
@@ -31,6 +31,19 @@ void nl80211_send_auth_timeout(struct cfg80211_registered_device *rdev, | |||
31 | void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, | 31 | void nl80211_send_assoc_timeout(struct cfg80211_registered_device *rdev, |
32 | struct net_device *netdev, | 32 | struct net_device *netdev, |
33 | const u8 *addr, gfp_t gfp); | 33 | const u8 *addr, gfp_t gfp); |
34 | void nl80211_send_connect_result(struct cfg80211_registered_device *rdev, | ||
35 | struct net_device *netdev, const u8 *bssid, | ||
36 | const u8 *req_ie, size_t req_ie_len, | ||
37 | const u8 *resp_ie, size_t resp_ie_len, | ||
38 | u16 status, gfp_t gfp); | ||
39 | void nl80211_send_roamed(struct cfg80211_registered_device *rdev, | ||
40 | struct net_device *netdev, const u8 *bssid, | ||
41 | const u8 *req_ie, size_t req_ie_len, | ||
42 | const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp); | ||
43 | void nl80211_send_disconnected(struct cfg80211_registered_device *rdev, | ||
44 | struct net_device *netdev, u16 reason, | ||
45 | u8 *ie, size_t ie_len, bool from_ap, gfp_t gfp); | ||
46 | |||
34 | void | 47 | void |
35 | nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, | 48 | nl80211_michael_mic_failure(struct cfg80211_registered_device *rdev, |
36 | struct net_device *netdev, const u8 *addr, | 49 | struct net_device *netdev, const u8 *addr, |
diff --git a/net/wireless/sme.c b/net/wireless/sme.c new file mode 100644 index 000000000000..fc117031d0bb --- /dev/null +++ b/net/wireless/sme.c | |||
@@ -0,0 +1,224 @@ | |||
1 | /* | ||
2 | * SME code for cfg80211's connect emulation. | ||
3 | * | ||
4 | * Copyright 2009 Johannes Berg <johannes@sipsolutions.net> | ||
5 | * Copyright (C) 2009 Intel Corporation. All rights reserved. | ||
6 | */ | ||
7 | |||
8 | #include <linux/etherdevice.h> | ||
9 | #include <linux/if_arp.h> | ||
10 | #include <linux/workqueue.h> | ||
11 | #include <net/cfg80211.h> | ||
12 | #include <net/rtnetlink.h> | ||
13 | #include "nl80211.h" | ||
14 | |||
15 | |||
16 | void cfg80211_connect_result(struct net_device *dev, const u8 *bssid, | ||
17 | const u8 *req_ie, size_t req_ie_len, | ||
18 | const u8 *resp_ie, size_t resp_ie_len, | ||
19 | u16 status, gfp_t gfp) | ||
20 | { | ||
21 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
22 | struct cfg80211_bss *bss; | ||
23 | #ifdef CONFIG_WIRELESS_EXT | ||
24 | union iwreq_data wrqu; | ||
25 | #endif | ||
26 | |||
27 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
28 | return; | ||
29 | |||
30 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTING)) | ||
31 | return; | ||
32 | |||
33 | if (wdev->current_bss) { | ||
34 | cfg80211_unhold_bss(wdev->current_bss); | ||
35 | cfg80211_put_bss(wdev->current_bss); | ||
36 | wdev->current_bss = NULL; | ||
37 | } | ||
38 | |||
39 | if (status == WLAN_STATUS_SUCCESS) { | ||
40 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, | ||
41 | wdev->ssid, wdev->ssid_len, | ||
42 | WLAN_CAPABILITY_ESS, | ||
43 | WLAN_CAPABILITY_ESS); | ||
44 | |||
45 | if (WARN_ON(!bss)) | ||
46 | return; | ||
47 | |||
48 | cfg80211_hold_bss(bss); | ||
49 | wdev->current_bss = bss; | ||
50 | |||
51 | wdev->sme_state = CFG80211_SME_CONNECTED; | ||
52 | } else { | ||
53 | wdev->sme_state = CFG80211_SME_IDLE; | ||
54 | } | ||
55 | |||
56 | nl80211_send_connect_result(wiphy_to_dev(wdev->wiphy), dev, bssid, | ||
57 | req_ie, req_ie_len, resp_ie, resp_ie_len, | ||
58 | status, gfp); | ||
59 | |||
60 | #ifdef CONFIG_WIRELESS_EXT | ||
61 | if (req_ie && status == WLAN_STATUS_SUCCESS) { | ||
62 | memset(&wrqu, 0, sizeof(wrqu)); | ||
63 | wrqu.data.length = req_ie_len; | ||
64 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); | ||
65 | } | ||
66 | |||
67 | if (resp_ie && status == WLAN_STATUS_SUCCESS) { | ||
68 | memset(&wrqu, 0, sizeof(wrqu)); | ||
69 | wrqu.data.length = resp_ie_len; | ||
70 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); | ||
71 | } | ||
72 | |||
73 | memset(&wrqu, 0, sizeof(wrqu)); | ||
74 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
75 | if (bssid) | ||
76 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | ||
77 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
78 | #endif | ||
79 | } | ||
80 | EXPORT_SYMBOL(cfg80211_connect_result); | ||
81 | |||
82 | void cfg80211_roamed(struct net_device *dev, const u8 *bssid, | ||
83 | const u8 *req_ie, size_t req_ie_len, | ||
84 | const u8 *resp_ie, size_t resp_ie_len, gfp_t gfp) | ||
85 | { | ||
86 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
87 | struct cfg80211_bss *bss; | ||
88 | #ifdef CONFIG_WIRELESS_EXT | ||
89 | union iwreq_data wrqu; | ||
90 | #endif | ||
91 | |||
92 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
93 | return; | ||
94 | |||
95 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) | ||
96 | return; | ||
97 | |||
98 | /* internal error -- how did we get to CONNECTED w/o BSS? */ | ||
99 | if (WARN_ON(!wdev->current_bss)) { | ||
100 | return; | ||
101 | } | ||
102 | |||
103 | cfg80211_unhold_bss(wdev->current_bss); | ||
104 | cfg80211_put_bss(wdev->current_bss); | ||
105 | wdev->current_bss = NULL; | ||
106 | |||
107 | bss = cfg80211_get_bss(wdev->wiphy, NULL, bssid, | ||
108 | wdev->ssid, wdev->ssid_len, | ||
109 | WLAN_CAPABILITY_ESS, WLAN_CAPABILITY_ESS); | ||
110 | |||
111 | if (WARN_ON(!bss)) | ||
112 | return; | ||
113 | |||
114 | cfg80211_hold_bss(bss); | ||
115 | wdev->current_bss = bss; | ||
116 | |||
117 | nl80211_send_roamed(wiphy_to_dev(wdev->wiphy), dev, bssid, | ||
118 | req_ie, req_ie_len, resp_ie, resp_ie_len, gfp); | ||
119 | |||
120 | #ifdef CONFIG_WIRELESS_EXT | ||
121 | if (req_ie) { | ||
122 | memset(&wrqu, 0, sizeof(wrqu)); | ||
123 | wrqu.data.length = req_ie_len; | ||
124 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, req_ie); | ||
125 | } | ||
126 | |||
127 | if (resp_ie) { | ||
128 | memset(&wrqu, 0, sizeof(wrqu)); | ||
129 | wrqu.data.length = resp_ie_len; | ||
130 | wireless_send_event(dev, IWEVASSOCRESPIE, &wrqu, resp_ie); | ||
131 | } | ||
132 | |||
133 | memset(&wrqu, 0, sizeof(wrqu)); | ||
134 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
135 | memcpy(wrqu.ap_addr.sa_data, bssid, ETH_ALEN); | ||
136 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
137 | #endif | ||
138 | } | ||
139 | EXPORT_SYMBOL(cfg80211_roamed); | ||
140 | |||
141 | static void __cfg80211_disconnected(struct net_device *dev, gfp_t gfp, | ||
142 | u8 *ie, size_t ie_len, u16 reason, | ||
143 | bool from_ap) | ||
144 | { | ||
145 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
146 | #ifdef CONFIG_WIRELESS_EXT | ||
147 | union iwreq_data wrqu; | ||
148 | #endif | ||
149 | |||
150 | if (WARN_ON(wdev->iftype != NL80211_IFTYPE_STATION)) | ||
151 | return; | ||
152 | |||
153 | if (WARN_ON(wdev->sme_state != CFG80211_SME_CONNECTED)) | ||
154 | return; | ||
155 | |||
156 | if (wdev->current_bss) { | ||
157 | cfg80211_unhold_bss(wdev->current_bss); | ||
158 | cfg80211_put_bss(wdev->current_bss); | ||
159 | } | ||
160 | |||
161 | wdev->current_bss = NULL; | ||
162 | wdev->sme_state = CFG80211_SME_IDLE; | ||
163 | |||
164 | nl80211_send_disconnected(wiphy_to_dev(wdev->wiphy), dev, | ||
165 | reason, ie, ie_len, from_ap, gfp); | ||
166 | |||
167 | #ifdef CONFIG_WIRELESS_EXT | ||
168 | memset(&wrqu, 0, sizeof(wrqu)); | ||
169 | wrqu.ap_addr.sa_family = ARPHRD_ETHER; | ||
170 | wireless_send_event(dev, SIOCGIWAP, &wrqu, NULL); | ||
171 | #endif | ||
172 | } | ||
173 | |||
174 | void cfg80211_disconnected(struct net_device *dev, u16 reason, | ||
175 | u8 *ie, size_t ie_len, gfp_t gfp) | ||
176 | { | ||
177 | __cfg80211_disconnected(dev, reason, ie, ie_len, true, gfp); | ||
178 | } | ||
179 | EXPORT_SYMBOL(cfg80211_disconnected); | ||
180 | |||
181 | int cfg80211_connect(struct cfg80211_registered_device *rdev, | ||
182 | struct net_device *dev, | ||
183 | struct cfg80211_connect_params *connect) | ||
184 | { | ||
185 | int err; | ||
186 | struct wireless_dev *wdev = dev->ieee80211_ptr; | ||
187 | |||
188 | if (wdev->sme_state != CFG80211_SME_IDLE) | ||
189 | return -EALREADY; | ||
190 | |||
191 | if (!rdev->ops->connect) { | ||
192 | return -EOPNOTSUPP; | ||
193 | } else { | ||
194 | wdev->sme_state = CFG80211_SME_CONNECTING; | ||
195 | err = rdev->ops->connect(&rdev->wiphy, dev, connect); | ||
196 | if (err) { | ||
197 | wdev->sme_state = CFG80211_SME_IDLE; | ||
198 | return err; | ||
199 | } | ||
200 | } | ||
201 | |||
202 | memcpy(wdev->ssid, connect->ssid, connect->ssid_len); | ||
203 | wdev->ssid_len = connect->ssid_len; | ||
204 | |||
205 | return 0; | ||
206 | } | ||
207 | |||
208 | int cfg80211_disconnect(struct cfg80211_registered_device *rdev, | ||
209 | struct net_device *dev, u16 reason) | ||
210 | { | ||
211 | int err; | ||
212 | |||
213 | if (!rdev->ops->disconnect) { | ||
214 | return -EOPNOTSUPP; | ||
215 | } else { | ||
216 | err = rdev->ops->disconnect(&rdev->wiphy, dev, reason); | ||
217 | if (err) | ||
218 | return err; | ||
219 | } | ||
220 | |||
221 | __cfg80211_disconnected(dev, 0, NULL, 0, false, GFP_KERNEL); | ||
222 | |||
223 | return 0; | ||
224 | } | ||