diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 160 |
1 files changed, 141 insertions, 19 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 04681a46eda8..b4f40fe84a01 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -53,6 +53,7 @@ enum nl80211_multicast_groups { | |||
53 | NL80211_MCGRP_SCAN, | 53 | NL80211_MCGRP_SCAN, |
54 | NL80211_MCGRP_REGULATORY, | 54 | NL80211_MCGRP_REGULATORY, |
55 | NL80211_MCGRP_MLME, | 55 | NL80211_MCGRP_MLME, |
56 | NL80211_MCGRP_VENDOR, | ||
56 | NL80211_MCGRP_TESTMODE /* keep last - ifdef! */ | 57 | NL80211_MCGRP_TESTMODE /* keep last - ifdef! */ |
57 | }; | 58 | }; |
58 | 59 | ||
@@ -61,6 +62,7 @@ static const struct genl_multicast_group nl80211_mcgrps[] = { | |||
61 | [NL80211_MCGRP_SCAN] = { .name = "scan", }, | 62 | [NL80211_MCGRP_SCAN] = { .name = "scan", }, |
62 | [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", }, | 63 | [NL80211_MCGRP_REGULATORY] = { .name = "regulatory", }, |
63 | [NL80211_MCGRP_MLME] = { .name = "mlme", }, | 64 | [NL80211_MCGRP_MLME] = { .name = "mlme", }, |
65 | [NL80211_MCGRP_VENDOR] = { .name = "vendor", }, | ||
64 | #ifdef CONFIG_NL80211_TESTMODE | 66 | #ifdef CONFIG_NL80211_TESTMODE |
65 | [NL80211_MCGRP_TESTMODE] = { .name = "testmode", } | 67 | [NL80211_MCGRP_TESTMODE] = { .name = "testmode", } |
66 | #endif | 68 | #endif |
@@ -380,6 +382,8 @@ static const struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] = { | |||
380 | [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, | 382 | [NL80211_ATTR_VENDOR_ID] = { .type = NLA_U32 }, |
381 | [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, | 383 | [NL80211_ATTR_VENDOR_SUBCMD] = { .type = NLA_U32 }, |
382 | [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, | 384 | [NL80211_ATTR_VENDOR_DATA] = { .type = NLA_BINARY }, |
385 | [NL80211_ATTR_QOS_MAP] = { .type = NLA_BINARY, | ||
386 | .len = IEEE80211_QOS_MAP_LEN_MAX }, | ||
383 | }; | 387 | }; |
384 | 388 | ||
385 | /* policy for the key attributes */ | 389 | /* policy for the key attributes */ |
@@ -1188,7 +1192,6 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1188 | struct nlattr *nl_bands, *nl_band; | 1192 | struct nlattr *nl_bands, *nl_band; |
1189 | struct nlattr *nl_freqs, *nl_freq; | 1193 | struct nlattr *nl_freqs, *nl_freq; |
1190 | struct nlattr *nl_cmds; | 1194 | struct nlattr *nl_cmds; |
1191 | struct nlattr *nl_vendor_cmds; | ||
1192 | enum ieee80211_band band; | 1195 | enum ieee80211_band band; |
1193 | struct ieee80211_channel *chan; | 1196 | struct ieee80211_channel *chan; |
1194 | int i; | 1197 | int i; |
@@ -1455,6 +1458,7 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1455 | if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) | 1458 | if (dev->wiphy.flags & WIPHY_FLAG_HAS_CHANNEL_SWITCH) |
1456 | CMD(channel_switch, CHANNEL_SWITCH); | 1459 | CMD(channel_switch, CHANNEL_SWITCH); |
1457 | } | 1460 | } |
1461 | CMD(set_qos_map, SET_QOS_MAP); | ||
1458 | 1462 | ||
1459 | #ifdef CONFIG_NL80211_TESTMODE | 1463 | #ifdef CONFIG_NL80211_TESTMODE |
1460 | CMD(testmode_cmd, TESTMODE); | 1464 | CMD(testmode_cmd, TESTMODE); |
@@ -1587,16 +1591,38 @@ static int nl80211_send_wiphy(struct cfg80211_registered_device *dev, | |||
1587 | state->split_start++; | 1591 | state->split_start++; |
1588 | break; | 1592 | break; |
1589 | case 11: | 1593 | case 11: |
1590 | nl_vendor_cmds = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); | 1594 | if (dev->wiphy.n_vendor_commands) { |
1591 | if (!nl_vendor_cmds) | 1595 | const struct nl80211_vendor_cmd_info *info; |
1592 | goto nla_put_failure; | 1596 | struct nlattr *nested; |
1597 | |||
1598 | nested = nla_nest_start(msg, NL80211_ATTR_VENDOR_DATA); | ||
1599 | if (!nested) | ||
1600 | goto nla_put_failure; | ||
1601 | |||
1602 | for (i = 0; i < dev->wiphy.n_vendor_commands; i++) { | ||
1603 | info = &dev->wiphy.vendor_commands[i].info; | ||
1604 | if (nla_put(msg, i + 1, sizeof(*info), info)) | ||
1605 | goto nla_put_failure; | ||
1606 | } | ||
1607 | nla_nest_end(msg, nested); | ||
1608 | } | ||
1609 | |||
1610 | if (dev->wiphy.n_vendor_events) { | ||
1611 | const struct nl80211_vendor_cmd_info *info; | ||
1612 | struct nlattr *nested; | ||
1593 | 1613 | ||
1594 | for (i = 0; i < dev->wiphy.n_vendor_commands; i++) | 1614 | nested = nla_nest_start(msg, |
1595 | if (nla_put(msg, i + 1, | 1615 | NL80211_ATTR_VENDOR_EVENTS); |
1596 | sizeof(struct nl80211_vendor_cmd_info), | 1616 | if (!nested) |
1597 | &dev->wiphy.vendor_commands[i].info)) | ||
1598 | goto nla_put_failure; | 1617 | goto nla_put_failure; |
1599 | nla_nest_end(msg, nl_vendor_cmds); | 1618 | |
1619 | for (i = 0; i < dev->wiphy.n_vendor_events; i++) { | ||
1620 | info = &dev->wiphy.vendor_events[i]; | ||
1621 | if (nla_put(msg, i + 1, sizeof(*info), info)) | ||
1622 | goto nla_put_failure; | ||
1623 | } | ||
1624 | nla_nest_end(msg, nested); | ||
1625 | } | ||
1600 | 1626 | ||
1601 | /* done */ | 1627 | /* done */ |
1602 | state->split_start = 0; | 1628 | state->split_start = 0; |
@@ -6726,7 +6752,9 @@ static struct sk_buff * | |||
6726 | __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, | 6752 | __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, |
6727 | int approxlen, u32 portid, u32 seq, | 6753 | int approxlen, u32 portid, u32 seq, |
6728 | enum nl80211_commands cmd, | 6754 | enum nl80211_commands cmd, |
6729 | enum nl80211_attrs attr, gfp_t gfp) | 6755 | enum nl80211_attrs attr, |
6756 | const struct nl80211_vendor_cmd_info *info, | ||
6757 | gfp_t gfp) | ||
6730 | { | 6758 | { |
6731 | struct sk_buff *skb; | 6759 | struct sk_buff *skb; |
6732 | void *hdr; | 6760 | void *hdr; |
@@ -6744,6 +6772,16 @@ __cfg80211_alloc_vendor_skb(struct cfg80211_registered_device *rdev, | |||
6744 | 6772 | ||
6745 | if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) | 6773 | if (nla_put_u32(skb, NL80211_ATTR_WIPHY, rdev->wiphy_idx)) |
6746 | goto nla_put_failure; | 6774 | goto nla_put_failure; |
6775 | |||
6776 | if (info) { | ||
6777 | if (nla_put_u32(skb, NL80211_ATTR_VENDOR_ID, | ||
6778 | info->vendor_id)) | ||
6779 | goto nla_put_failure; | ||
6780 | if (nla_put_u32(skb, NL80211_ATTR_VENDOR_SUBCMD, | ||
6781 | info->subcmd)) | ||
6782 | goto nla_put_failure; | ||
6783 | } | ||
6784 | |||
6747 | data = nla_nest_start(skb, attr); | 6785 | data = nla_nest_start(skb, attr); |
6748 | 6786 | ||
6749 | ((void **)skb->cb)[0] = rdev; | 6787 | ((void **)skb->cb)[0] = rdev; |
@@ -6884,29 +6922,54 @@ static int nl80211_testmode_dump(struct sk_buff *skb, | |||
6884 | return err; | 6922 | return err; |
6885 | } | 6923 | } |
6886 | 6924 | ||
6887 | struct sk_buff *cfg80211_testmode_alloc_event_skb(struct wiphy *wiphy, | 6925 | struct sk_buff *__cfg80211_alloc_event_skb(struct wiphy *wiphy, |
6888 | int approxlen, gfp_t gfp) | 6926 | enum nl80211_commands cmd, |
6927 | enum nl80211_attrs attr, | ||
6928 | int vendor_event_idx, | ||
6929 | int approxlen, gfp_t gfp) | ||
6889 | { | 6930 | { |
6890 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); | 6931 | struct cfg80211_registered_device *rdev = wiphy_to_dev(wiphy); |
6932 | const struct nl80211_vendor_cmd_info *info; | ||
6933 | |||
6934 | switch (cmd) { | ||
6935 | case NL80211_CMD_TESTMODE: | ||
6936 | if (WARN_ON(vendor_event_idx != -1)) | ||
6937 | return NULL; | ||
6938 | info = NULL; | ||
6939 | break; | ||
6940 | case NL80211_CMD_VENDOR: | ||
6941 | if (WARN_ON(vendor_event_idx < 0 || | ||
6942 | vendor_event_idx >= wiphy->n_vendor_events)) | ||
6943 | return NULL; | ||
6944 | info = &wiphy->vendor_events[vendor_event_idx]; | ||
6945 | break; | ||
6946 | default: | ||
6947 | WARN_ON(1); | ||
6948 | return NULL; | ||
6949 | } | ||
6891 | 6950 | ||
6892 | return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, | 6951 | return __cfg80211_alloc_vendor_skb(rdev, approxlen, 0, 0, |
6893 | NL80211_CMD_TESTMODE, | 6952 | cmd, attr, info, gfp); |
6894 | NL80211_ATTR_TESTDATA, gfp); | ||
6895 | } | 6953 | } |
6896 | EXPORT_SYMBOL(cfg80211_testmode_alloc_event_skb); | 6954 | EXPORT_SYMBOL(__cfg80211_alloc_event_skb); |
6897 | 6955 | ||
6898 | void cfg80211_testmode_event(struct sk_buff *skb, gfp_t gfp) | 6956 | void __cfg80211_send_event_skb(struct sk_buff *skb, gfp_t gfp) |
6899 | { | 6957 | { |
6900 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; | 6958 | struct cfg80211_registered_device *rdev = ((void **)skb->cb)[0]; |
6901 | void *hdr = ((void **)skb->cb)[1]; | 6959 | void *hdr = ((void **)skb->cb)[1]; |
6902 | struct nlattr *data = ((void **)skb->cb)[2]; | 6960 | struct nlattr *data = ((void **)skb->cb)[2]; |
6961 | enum nl80211_multicast_groups mcgrp = NL80211_MCGRP_TESTMODE; | ||
6903 | 6962 | ||
6904 | nla_nest_end(skb, data); | 6963 | nla_nest_end(skb, data); |
6905 | genlmsg_end(skb, hdr); | 6964 | genlmsg_end(skb, hdr); |
6965 | |||
6966 | if (data->nla_type == NL80211_ATTR_VENDOR_DATA) | ||
6967 | mcgrp = NL80211_MCGRP_VENDOR; | ||
6968 | |||
6906 | genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, | 6969 | genlmsg_multicast_netns(&nl80211_fam, wiphy_net(&rdev->wiphy), skb, 0, |
6907 | NL80211_MCGRP_TESTMODE, gfp); | 6970 | mcgrp, gfp); |
6908 | } | 6971 | } |
6909 | EXPORT_SYMBOL(cfg80211_testmode_event); | 6972 | EXPORT_SYMBOL(__cfg80211_send_event_skb); |
6910 | #endif | 6973 | #endif |
6911 | 6974 | ||
6912 | static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) | 6975 | static int nl80211_connect(struct sk_buff *skb, struct genl_info *info) |
@@ -9039,7 +9102,7 @@ struct sk_buff *__cfg80211_alloc_reply_skb(struct wiphy *wiphy, | |||
9039 | return __cfg80211_alloc_vendor_skb(rdev, approxlen, | 9102 | return __cfg80211_alloc_vendor_skb(rdev, approxlen, |
9040 | rdev->cur_cmd_info->snd_portid, | 9103 | rdev->cur_cmd_info->snd_portid, |
9041 | rdev->cur_cmd_info->snd_seq, | 9104 | rdev->cur_cmd_info->snd_seq, |
9042 | cmd, attr, GFP_KERNEL); | 9105 | cmd, attr, NULL, GFP_KERNEL); |
9043 | } | 9106 | } |
9044 | EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); | 9107 | EXPORT_SYMBOL(__cfg80211_alloc_reply_skb); |
9045 | 9108 | ||
@@ -9061,6 +9124,57 @@ int cfg80211_vendor_cmd_reply(struct sk_buff *skb) | |||
9061 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); | 9124 | EXPORT_SYMBOL_GPL(cfg80211_vendor_cmd_reply); |
9062 | 9125 | ||
9063 | 9126 | ||
9127 | static int nl80211_set_qos_map(struct sk_buff *skb, | ||
9128 | struct genl_info *info) | ||
9129 | { | ||
9130 | struct cfg80211_registered_device *rdev = info->user_ptr[0]; | ||
9131 | struct cfg80211_qos_map *qos_map = NULL; | ||
9132 | struct net_device *dev = info->user_ptr[1]; | ||
9133 | u8 *pos, len, num_des, des_len, des; | ||
9134 | int ret; | ||
9135 | |||
9136 | if (!rdev->ops->set_qos_map) | ||
9137 | return -EOPNOTSUPP; | ||
9138 | |||
9139 | if (info->attrs[NL80211_ATTR_QOS_MAP]) { | ||
9140 | pos = nla_data(info->attrs[NL80211_ATTR_QOS_MAP]); | ||
9141 | len = nla_len(info->attrs[NL80211_ATTR_QOS_MAP]); | ||
9142 | |||
9143 | if (len % 2 || len < IEEE80211_QOS_MAP_LEN_MIN || | ||
9144 | len > IEEE80211_QOS_MAP_LEN_MAX) | ||
9145 | return -EINVAL; | ||
9146 | |||
9147 | qos_map = kzalloc(sizeof(struct cfg80211_qos_map), GFP_KERNEL); | ||
9148 | if (!qos_map) | ||
9149 | return -ENOMEM; | ||
9150 | |||
9151 | num_des = (len - IEEE80211_QOS_MAP_LEN_MIN) >> 1; | ||
9152 | if (num_des) { | ||
9153 | des_len = num_des * | ||
9154 | sizeof(struct cfg80211_dscp_exception); | ||
9155 | memcpy(qos_map->dscp_exception, pos, des_len); | ||
9156 | qos_map->num_des = num_des; | ||
9157 | for (des = 0; des < num_des; des++) { | ||
9158 | if (qos_map->dscp_exception[des].up > 7) { | ||
9159 | kfree(qos_map); | ||
9160 | return -EINVAL; | ||
9161 | } | ||
9162 | } | ||
9163 | pos += des_len; | ||
9164 | } | ||
9165 | memcpy(qos_map->up, pos, IEEE80211_QOS_MAP_LEN_MIN); | ||
9166 | } | ||
9167 | |||
9168 | wdev_lock(dev->ieee80211_ptr); | ||
9169 | ret = nl80211_key_allowed(dev->ieee80211_ptr); | ||
9170 | if (!ret) | ||
9171 | ret = rdev_set_qos_map(rdev, dev, qos_map); | ||
9172 | wdev_unlock(dev->ieee80211_ptr); | ||
9173 | |||
9174 | kfree(qos_map); | ||
9175 | return ret; | ||
9176 | } | ||
9177 | |||
9064 | #define NL80211_FLAG_NEED_WIPHY 0x01 | 9178 | #define NL80211_FLAG_NEED_WIPHY 0x01 |
9065 | #define NL80211_FLAG_NEED_NETDEV 0x02 | 9179 | #define NL80211_FLAG_NEED_NETDEV 0x02 |
9066 | #define NL80211_FLAG_NEED_RTNL 0x04 | 9180 | #define NL80211_FLAG_NEED_RTNL 0x04 |
@@ -9793,6 +9907,14 @@ static const struct genl_ops nl80211_ops[] = { | |||
9793 | .internal_flags = NL80211_FLAG_NEED_WIPHY | | 9907 | .internal_flags = NL80211_FLAG_NEED_WIPHY | |
9794 | NL80211_FLAG_NEED_RTNL, | 9908 | NL80211_FLAG_NEED_RTNL, |
9795 | }, | 9909 | }, |
9910 | { | ||
9911 | .cmd = NL80211_CMD_SET_QOS_MAP, | ||
9912 | .doit = nl80211_set_qos_map, | ||
9913 | .policy = nl80211_policy, | ||
9914 | .flags = GENL_ADMIN_PERM, | ||
9915 | .internal_flags = NL80211_FLAG_NEED_NETDEV_UP | | ||
9916 | NL80211_FLAG_NEED_RTNL, | ||
9917 | }, | ||
9796 | }; | 9918 | }; |
9797 | 9919 | ||
9798 | /* notification functions */ | 9920 | /* notification functions */ |