diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 218 |
1 files changed, 217 insertions, 1 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 60f854377f90..ff857f10cb85 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -141,6 +141,8 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, | 141 | [NL80211_ATTR_4ADDR] = { .type = NLA_U8 }, |
142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, | 142 | [NL80211_ATTR_PMKID] = { .type = NLA_BINARY, |
143 | .len = WLAN_PMKID_LEN }, | 143 | .len = WLAN_PMKID_LEN }, |
144 | [NL80211_ATTR_DURATION] = { .type = NLA_U32 }, | ||
145 | [NL80211_ATTR_COOKIE] = { .type = NLA_U64 }, | ||
144 | }; | 146 | }; |
145 | 147 | ||
146 | /* policy for the attributes */ | 148 | /* policy for the attributes */ |
@@ -569,6 +571,7 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
569 | CMD(set_pmksa, SET_PMKSA); | 571 | CMD(set_pmksa, SET_PMKSA); |
570 | CMD(del_pmksa, DEL_PMKSA); | 572 | CMD(del_pmksa, DEL_PMKSA); |
571 | CMD(flush_pmksa, FLUSH_PMKSA); | 573 | CMD(flush_pmksa, FLUSH_PMKSA); |
574 | CMD(remain_on_channel, REMAIN_ON_CHANNEL); | ||
572 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { | 575 | if (dev->wiphy.flags & WIPHY_FLAG_NETNS_OK) { |
573 | i++; | 576 | i++; |
574 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); | 577 | NLA_PUT_U32(msg, i, NL80211_CMD_SET_WIPHY_NETNS); |
@@ -4283,6 +4286,143 @@ static int nl80211_flush_pmksa(struct sk_buff *skb, struct genl_info *info) | |||
4283 | 4286 | ||
4284 | } | 4287 | } |
4285 | 4288 | ||
4289 | static int nl80211_remain_on_channel(struct sk_buff *skb, | ||
4290 | struct genl_info *info) | ||
4291 | { | ||
4292 | struct cfg80211_registered_device *rdev; | ||
4293 | struct net_device *dev; | ||
4294 | struct ieee80211_channel *chan; | ||
4295 | struct sk_buff *msg; | ||
4296 | void *hdr; | ||
4297 | u64 cookie; | ||
4298 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
4299 | u32 freq, duration; | ||
4300 | int err; | ||
4301 | |||
4302 | if (!info->attrs[NL80211_ATTR_WIPHY_FREQ] || | ||
4303 | !info->attrs[NL80211_ATTR_DURATION]) | ||
4304 | return -EINVAL; | ||
4305 | |||
4306 | duration = nla_get_u32(info->attrs[NL80211_ATTR_DURATION]); | ||
4307 | |||
4308 | /* | ||
4309 | * We should be on that channel for at least one jiffie, | ||
4310 | * and more than 5 seconds seems excessive. | ||
4311 | */ | ||
4312 | if (!duration || !msecs_to_jiffies(duration) || duration > 5000) | ||
4313 | return -EINVAL; | ||
4314 | |||
4315 | rtnl_lock(); | ||
4316 | |||
4317 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4318 | if (err) | ||
4319 | goto unlock_rtnl; | ||
4320 | |||
4321 | if (!rdev->ops->remain_on_channel) { | ||
4322 | err = -EOPNOTSUPP; | ||
4323 | goto out; | ||
4324 | } | ||
4325 | |||
4326 | if (!netif_running(dev)) { | ||
4327 | err = -ENETDOWN; | ||
4328 | goto out; | ||
4329 | } | ||
4330 | |||
4331 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
4332 | channel_type = nla_get_u32( | ||
4333 | info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
4334 | if (channel_type != NL80211_CHAN_NO_HT && | ||
4335 | channel_type != NL80211_CHAN_HT20 && | ||
4336 | channel_type != NL80211_CHAN_HT40PLUS && | ||
4337 | channel_type != NL80211_CHAN_HT40MINUS) | ||
4338 | err = -EINVAL; | ||
4339 | goto out; | ||
4340 | } | ||
4341 | |||
4342 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
4343 | chan = rdev_freq_to_chan(rdev, freq, channel_type); | ||
4344 | if (chan == NULL) { | ||
4345 | err = -EINVAL; | ||
4346 | goto out; | ||
4347 | } | ||
4348 | |||
4349 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, GFP_KERNEL); | ||
4350 | if (!msg) { | ||
4351 | err = -ENOMEM; | ||
4352 | goto out; | ||
4353 | } | ||
4354 | |||
4355 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
4356 | NL80211_CMD_REMAIN_ON_CHANNEL); | ||
4357 | |||
4358 | if (IS_ERR(hdr)) { | ||
4359 | err = PTR_ERR(hdr); | ||
4360 | goto free_msg; | ||
4361 | } | ||
4362 | |||
4363 | err = rdev->ops->remain_on_channel(&rdev->wiphy, dev, chan, | ||
4364 | channel_type, duration, &cookie); | ||
4365 | |||
4366 | if (err) | ||
4367 | goto free_msg; | ||
4368 | |||
4369 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
4370 | |||
4371 | genlmsg_end(msg, hdr); | ||
4372 | err = genlmsg_reply(msg, info); | ||
4373 | goto out; | ||
4374 | |||
4375 | nla_put_failure: | ||
4376 | err = -ENOBUFS; | ||
4377 | free_msg: | ||
4378 | nlmsg_free(msg); | ||
4379 | out: | ||
4380 | cfg80211_unlock_rdev(rdev); | ||
4381 | dev_put(dev); | ||
4382 | unlock_rtnl: | ||
4383 | rtnl_unlock(); | ||
4384 | return err; | ||
4385 | } | ||
4386 | |||
4387 | static int nl80211_cancel_remain_on_channel(struct sk_buff *skb, | ||
4388 | struct genl_info *info) | ||
4389 | { | ||
4390 | struct cfg80211_registered_device *rdev; | ||
4391 | struct net_device *dev; | ||
4392 | u64 cookie; | ||
4393 | int err; | ||
4394 | |||
4395 | if (!info->attrs[NL80211_ATTR_COOKIE]) | ||
4396 | return -EINVAL; | ||
4397 | |||
4398 | rtnl_lock(); | ||
4399 | |||
4400 | err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev); | ||
4401 | if (err) | ||
4402 | goto unlock_rtnl; | ||
4403 | |||
4404 | if (!rdev->ops->cancel_remain_on_channel) { | ||
4405 | err = -EOPNOTSUPP; | ||
4406 | goto out; | ||
4407 | } | ||
4408 | |||
4409 | if (!netif_running(dev)) { | ||
4410 | err = -ENETDOWN; | ||
4411 | goto out; | ||
4412 | } | ||
4413 | |||
4414 | cookie = nla_get_u64(info->attrs[NL80211_ATTR_COOKIE]); | ||
4415 | |||
4416 | err = rdev->ops->cancel_remain_on_channel(&rdev->wiphy, dev, cookie); | ||
4417 | |||
4418 | out: | ||
4419 | cfg80211_unlock_rdev(rdev); | ||
4420 | dev_put(dev); | ||
4421 | unlock_rtnl: | ||
4422 | rtnl_unlock(); | ||
4423 | return err; | ||
4424 | } | ||
4425 | |||
4286 | static struct genl_ops nl80211_ops[] = { | 4426 | static struct genl_ops nl80211_ops[] = { |
4287 | { | 4427 | { |
4288 | .cmd = NL80211_CMD_GET_WIPHY, | 4428 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -4545,8 +4685,20 @@ static struct genl_ops nl80211_ops[] = { | |||
4545 | .policy = nl80211_policy, | 4685 | .policy = nl80211_policy, |
4546 | .flags = GENL_ADMIN_PERM, | 4686 | .flags = GENL_ADMIN_PERM, |
4547 | }, | 4687 | }, |
4548 | 4688 | { | |
4689 | .cmd = NL80211_CMD_REMAIN_ON_CHANNEL, | ||
4690 | .doit = nl80211_remain_on_channel, | ||
4691 | .policy = nl80211_policy, | ||
4692 | .flags = GENL_ADMIN_PERM, | ||
4693 | }, | ||
4694 | { | ||
4695 | .cmd = NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
4696 | .doit = nl80211_cancel_remain_on_channel, | ||
4697 | .policy = nl80211_policy, | ||
4698 | .flags = GENL_ADMIN_PERM, | ||
4699 | }, | ||
4549 | }; | 4700 | }; |
4701 | |||
4550 | static struct genl_multicast_group nl80211_mlme_mcgrp = { | 4702 | static struct genl_multicast_group nl80211_mlme_mcgrp = { |
4551 | .name = "mlme", | 4703 | .name = "mlme", |
4552 | }; | 4704 | }; |
@@ -5134,6 +5286,70 @@ nla_put_failure: | |||
5134 | nlmsg_free(msg); | 5286 | nlmsg_free(msg); |
5135 | } | 5287 | } |
5136 | 5288 | ||
5289 | static void nl80211_send_remain_on_chan_event( | ||
5290 | int cmd, struct cfg80211_registered_device *rdev, | ||
5291 | struct net_device *netdev, u64 cookie, | ||
5292 | struct ieee80211_channel *chan, | ||
5293 | enum nl80211_channel_type channel_type, | ||
5294 | unsigned int duration, gfp_t gfp) | ||
5295 | { | ||
5296 | struct sk_buff *msg; | ||
5297 | void *hdr; | ||
5298 | |||
5299 | msg = nlmsg_new(NLMSG_DEFAULT_SIZE, gfp); | ||
5300 | if (!msg) | ||
5301 | return; | ||
5302 | |||
5303 | hdr = nl80211hdr_put(msg, 0, 0, 0, cmd); | ||
5304 | if (!hdr) { | ||
5305 | nlmsg_free(msg); | ||
5306 | return; | ||
5307 | } | ||
5308 | |||
5309 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, rdev->wiphy_idx); | ||
5310 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, netdev->ifindex); | ||
5311 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_FREQ, chan->center_freq); | ||
5312 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY_CHANNEL_TYPE, channel_type); | ||
5313 | NLA_PUT_U64(msg, NL80211_ATTR_COOKIE, cookie); | ||
5314 | |||
5315 | if (cmd == NL80211_CMD_REMAIN_ON_CHANNEL) | ||
5316 | NLA_PUT_U32(msg, NL80211_ATTR_DURATION, duration); | ||
5317 | |||
5318 | if (genlmsg_end(msg, hdr) < 0) { | ||
5319 | nlmsg_free(msg); | ||
5320 | return; | ||
5321 | } | ||
5322 | |||
5323 | genlmsg_multicast_netns(wiphy_net(&rdev->wiphy), msg, 0, | ||
5324 | nl80211_mlme_mcgrp.id, gfp); | ||
5325 | return; | ||
5326 | |||
5327 | nla_put_failure: | ||
5328 | genlmsg_cancel(msg, hdr); | ||
5329 | nlmsg_free(msg); | ||
5330 | } | ||
5331 | |||
5332 | void nl80211_send_remain_on_channel(struct cfg80211_registered_device *rdev, | ||
5333 | struct net_device *netdev, u64 cookie, | ||
5334 | struct ieee80211_channel *chan, | ||
5335 | enum nl80211_channel_type channel_type, | ||
5336 | unsigned int duration, gfp_t gfp) | ||
5337 | { | ||
5338 | nl80211_send_remain_on_chan_event(NL80211_CMD_REMAIN_ON_CHANNEL, | ||
5339 | rdev, netdev, cookie, chan, | ||
5340 | channel_type, duration, gfp); | ||
5341 | } | ||
5342 | |||
5343 | void nl80211_send_remain_on_channel_cancel( | ||
5344 | struct cfg80211_registered_device *rdev, struct net_device *netdev, | ||
5345 | u64 cookie, struct ieee80211_channel *chan, | ||
5346 | enum nl80211_channel_type channel_type, gfp_t gfp) | ||
5347 | { | ||
5348 | nl80211_send_remain_on_chan_event(NL80211_CMD_CANCEL_REMAIN_ON_CHANNEL, | ||
5349 | rdev, netdev, cookie, chan, | ||
5350 | channel_type, 0, gfp); | ||
5351 | } | ||
5352 | |||
5137 | /* initialisation/exit functions */ | 5353 | /* initialisation/exit functions */ |
5138 | 5354 | ||
5139 | int nl80211_init(void) | 5355 | int nl80211_init(void) |