diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 280 |
1 files changed, 263 insertions, 17 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 59eb2cf42e5f..572793c8c7ab 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include <net/cfg80211.h> | 18 | #include <net/cfg80211.h> |
19 | #include "core.h" | 19 | #include "core.h" |
20 | #include "nl80211.h" | 20 | #include "nl80211.h" |
21 | #include "reg.h" | ||
21 | 22 | ||
22 | /* the netlink family */ | 23 | /* the netlink family */ |
23 | static struct genl_family nl80211_fam = { | 24 | static struct genl_family nl80211_fam = { |
@@ -87,6 +88,16 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
87 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, | 88 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, |
88 | .len = IEEE80211_MAX_MESH_ID_LEN }, | 89 | .len = IEEE80211_MAX_MESH_ID_LEN }, |
89 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, | 90 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, |
91 | |||
92 | [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, | ||
93 | [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, | ||
94 | |||
95 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, | ||
96 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, | ||
97 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, | ||
98 | |||
99 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | ||
100 | .len = NL80211_HT_CAPABILITY_LEN }, | ||
90 | }; | 101 | }; |
91 | 102 | ||
92 | /* message building helper */ | 103 | /* message building helper */ |
@@ -106,10 +117,12 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
106 | struct nlattr *nl_bands, *nl_band; | 117 | struct nlattr *nl_bands, *nl_band; |
107 | struct nlattr *nl_freqs, *nl_freq; | 118 | struct nlattr *nl_freqs, *nl_freq; |
108 | struct nlattr *nl_rates, *nl_rate; | 119 | struct nlattr *nl_rates, *nl_rate; |
120 | struct nlattr *nl_modes; | ||
109 | enum ieee80211_band band; | 121 | enum ieee80211_band band; |
110 | struct ieee80211_channel *chan; | 122 | struct ieee80211_channel *chan; |
111 | struct ieee80211_rate *rate; | 123 | struct ieee80211_rate *rate; |
112 | int i; | 124 | int i; |
125 | u16 ifmodes = dev->wiphy.interface_modes; | ||
113 | 126 | ||
114 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); | 127 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_WIPHY); |
115 | if (!hdr) | 128 | if (!hdr) |
@@ -118,6 +131,20 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
118 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); | 131 | NLA_PUT_U32(msg, NL80211_ATTR_WIPHY, dev->idx); |
119 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); | 132 | NLA_PUT_STRING(msg, NL80211_ATTR_WIPHY_NAME, wiphy_name(&dev->wiphy)); |
120 | 133 | ||
134 | nl_modes = nla_nest_start(msg, NL80211_ATTR_SUPPORTED_IFTYPES); | ||
135 | if (!nl_modes) | ||
136 | goto nla_put_failure; | ||
137 | |||
138 | i = 0; | ||
139 | while (ifmodes) { | ||
140 | if (ifmodes & 1) | ||
141 | NLA_PUT_FLAG(msg, i); | ||
142 | ifmodes >>= 1; | ||
143 | i++; | ||
144 | } | ||
145 | |||
146 | nla_nest_end(msg, nl_modes); | ||
147 | |||
121 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); | 148 | nl_bands = nla_nest_start(msg, NL80211_ATTR_WIPHY_BANDS); |
122 | if (!nl_bands) | 149 | if (!nl_bands) |
123 | goto nla_put_failure; | 150 | goto nla_put_failure; |
@@ -272,7 +299,7 @@ static int nl80211_send_iface(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
272 | 299 | ||
273 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | 300 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); |
274 | NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); | 301 | NLA_PUT_STRING(msg, NL80211_ATTR_IFNAME, dev->name); |
275 | /* TODO: interface type */ | 302 | NLA_PUT_U32(msg, NL80211_ATTR_IFTYPE, dev->ieee80211_ptr->iftype); |
276 | return genlmsg_end(msg, hdr); | 303 | return genlmsg_end(msg, hdr); |
277 | 304 | ||
278 | nla_put_failure: | 305 | nla_put_failure: |
@@ -391,40 +418,56 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
391 | int err, ifindex; | 418 | int err, ifindex; |
392 | enum nl80211_iftype type; | 419 | enum nl80211_iftype type; |
393 | struct net_device *dev; | 420 | struct net_device *dev; |
394 | u32 flags; | 421 | u32 _flags, *flags = NULL; |
395 | 422 | ||
396 | memset(¶ms, 0, sizeof(params)); | 423 | memset(¶ms, 0, sizeof(params)); |
397 | 424 | ||
398 | if (info->attrs[NL80211_ATTR_IFTYPE]) { | ||
399 | type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); | ||
400 | if (type > NL80211_IFTYPE_MAX) | ||
401 | return -EINVAL; | ||
402 | } else | ||
403 | return -EINVAL; | ||
404 | |||
405 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 425 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
406 | if (err) | 426 | if (err) |
407 | return err; | 427 | return err; |
408 | ifindex = dev->ifindex; | 428 | ifindex = dev->ifindex; |
429 | type = dev->ieee80211_ptr->iftype; | ||
409 | dev_put(dev); | 430 | dev_put(dev); |
410 | 431 | ||
411 | if (!drv->ops->change_virtual_intf) { | 432 | err = -EINVAL; |
433 | if (info->attrs[NL80211_ATTR_IFTYPE]) { | ||
434 | type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); | ||
435 | if (type > NL80211_IFTYPE_MAX) | ||
436 | goto unlock; | ||
437 | } | ||
438 | |||
439 | if (!drv->ops->change_virtual_intf || | ||
440 | !(drv->wiphy.interface_modes & (1 << type))) { | ||
412 | err = -EOPNOTSUPP; | 441 | err = -EOPNOTSUPP; |
413 | goto unlock; | 442 | goto unlock; |
414 | } | 443 | } |
415 | 444 | ||
416 | if (type == NL80211_IFTYPE_MESH_POINT && | 445 | if (info->attrs[NL80211_ATTR_MESH_ID]) { |
417 | info->attrs[NL80211_ATTR_MESH_ID]) { | 446 | if (type != NL80211_IFTYPE_MESH_POINT) { |
447 | err = -EINVAL; | ||
448 | goto unlock; | ||
449 | } | ||
418 | params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); | 450 | params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); |
419 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); | 451 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); |
420 | } | 452 | } |
421 | 453 | ||
454 | if (info->attrs[NL80211_ATTR_MNTR_FLAGS]) { | ||
455 | if (type != NL80211_IFTYPE_MONITOR) { | ||
456 | err = -EINVAL; | ||
457 | goto unlock; | ||
458 | } | ||
459 | err = parse_monitor_flags(info->attrs[NL80211_ATTR_MNTR_FLAGS], | ||
460 | &_flags); | ||
461 | if (!err) | ||
462 | flags = &_flags; | ||
463 | } | ||
422 | rtnl_lock(); | 464 | rtnl_lock(); |
423 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | ||
424 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | ||
425 | &flags); | ||
426 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, | 465 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, |
427 | type, err ? NULL : &flags, ¶ms); | 466 | type, flags, ¶ms); |
467 | |||
468 | dev = __dev_get_by_index(&init_net, ifindex); | ||
469 | WARN_ON(!dev || (!err && dev->ieee80211_ptr->iftype != type)); | ||
470 | |||
428 | rtnl_unlock(); | 471 | rtnl_unlock(); |
429 | 472 | ||
430 | unlock: | 473 | unlock: |
@@ -455,7 +498,8 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
455 | if (IS_ERR(drv)) | 498 | if (IS_ERR(drv)) |
456 | return PTR_ERR(drv); | 499 | return PTR_ERR(drv); |
457 | 500 | ||
458 | if (!drv->ops->add_virtual_intf) { | 501 | if (!drv->ops->add_virtual_intf || |
502 | !(drv->wiphy.interface_modes & (1 << type))) { | ||
459 | err = -EOPNOTSUPP; | 503 | err = -EOPNOTSUPP; |
460 | goto unlock; | 504 | goto unlock; |
461 | } | 505 | } |
@@ -1125,6 +1169,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
1125 | params.listen_interval = | 1169 | params.listen_interval = |
1126 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 1170 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
1127 | 1171 | ||
1172 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | ||
1173 | params.ht_capa = | ||
1174 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); | ||
1175 | |||
1128 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], | 1176 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], |
1129 | ¶ms.station_flags)) | 1177 | ¶ms.station_flags)) |
1130 | return -EINVAL; | 1178 | return -EINVAL; |
@@ -1188,6 +1236,9 @@ static int nl80211_new_station(struct sk_buff *skb, struct genl_info *info) | |||
1188 | params.listen_interval = | 1236 | params.listen_interval = |
1189 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); | 1237 | nla_get_u16(info->attrs[NL80211_ATTR_STA_LISTEN_INTERVAL]); |
1190 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); | 1238 | params.aid = nla_get_u16(info->attrs[NL80211_ATTR_STA_AID]); |
1239 | if (info->attrs[NL80211_ATTR_HT_CAPABILITY]) | ||
1240 | params.ht_capa = | ||
1241 | nla_data(info->attrs[NL80211_ATTR_HT_CAPABILITY]); | ||
1191 | 1242 | ||
1192 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], | 1243 | if (parse_station_flags(info->attrs[NL80211_ATTR_STA_FLAGS], |
1193 | ¶ms.station_flags)) | 1244 | ¶ms.station_flags)) |
@@ -1525,6 +1576,183 @@ static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) | |||
1525 | return err; | 1576 | return err; |
1526 | } | 1577 | } |
1527 | 1578 | ||
1579 | static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | ||
1580 | { | ||
1581 | struct cfg80211_registered_device *drv; | ||
1582 | int err; | ||
1583 | struct net_device *dev; | ||
1584 | struct bss_parameters params; | ||
1585 | |||
1586 | memset(¶ms, 0, sizeof(params)); | ||
1587 | /* default to not changing parameters */ | ||
1588 | params.use_cts_prot = -1; | ||
1589 | params.use_short_preamble = -1; | ||
1590 | params.use_short_slot_time = -1; | ||
1591 | |||
1592 | if (info->attrs[NL80211_ATTR_BSS_CTS_PROT]) | ||
1593 | params.use_cts_prot = | ||
1594 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_CTS_PROT]); | ||
1595 | if (info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]) | ||
1596 | params.use_short_preamble = | ||
1597 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_PREAMBLE]); | ||
1598 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) | ||
1599 | params.use_short_slot_time = | ||
1600 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); | ||
1601 | |||
1602 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
1603 | if (err) | ||
1604 | return err; | ||
1605 | |||
1606 | if (!drv->ops->change_bss) { | ||
1607 | err = -EOPNOTSUPP; | ||
1608 | goto out; | ||
1609 | } | ||
1610 | |||
1611 | rtnl_lock(); | ||
1612 | err = drv->ops->change_bss(&drv->wiphy, dev, ¶ms); | ||
1613 | rtnl_unlock(); | ||
1614 | |||
1615 | out: | ||
1616 | cfg80211_put_dev(drv); | ||
1617 | dev_put(dev); | ||
1618 | return err; | ||
1619 | } | ||
1620 | |||
1621 | static const struct nla_policy | ||
1622 | reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | ||
1623 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, | ||
1624 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, | ||
1625 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, | ||
1626 | [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, | ||
1627 | [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, | ||
1628 | [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, | ||
1629 | }; | ||
1630 | |||
1631 | static int parse_reg_rule(struct nlattr *tb[], | ||
1632 | struct ieee80211_reg_rule *reg_rule) | ||
1633 | { | ||
1634 | struct ieee80211_freq_range *freq_range = ®_rule->freq_range; | ||
1635 | struct ieee80211_power_rule *power_rule = ®_rule->power_rule; | ||
1636 | |||
1637 | if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) | ||
1638 | return -EINVAL; | ||
1639 | if (!tb[NL80211_ATTR_FREQ_RANGE_START]) | ||
1640 | return -EINVAL; | ||
1641 | if (!tb[NL80211_ATTR_FREQ_RANGE_END]) | ||
1642 | return -EINVAL; | ||
1643 | if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) | ||
1644 | return -EINVAL; | ||
1645 | if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) | ||
1646 | return -EINVAL; | ||
1647 | |||
1648 | reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); | ||
1649 | |||
1650 | freq_range->start_freq_khz = | ||
1651 | nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); | ||
1652 | freq_range->end_freq_khz = | ||
1653 | nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); | ||
1654 | freq_range->max_bandwidth_khz = | ||
1655 | nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); | ||
1656 | |||
1657 | power_rule->max_eirp = | ||
1658 | nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); | ||
1659 | |||
1660 | if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) | ||
1661 | power_rule->max_antenna_gain = | ||
1662 | nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); | ||
1663 | |||
1664 | return 0; | ||
1665 | } | ||
1666 | |||
1667 | static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | ||
1668 | { | ||
1669 | int r; | ||
1670 | char *data = NULL; | ||
1671 | |||
1672 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | ||
1673 | return -EINVAL; | ||
1674 | |||
1675 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | ||
1676 | |||
1677 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
1678 | /* We ignore world regdom requests with the old regdom setup */ | ||
1679 | if (is_world_regdom(data)) | ||
1680 | return -EINVAL; | ||
1681 | #endif | ||
1682 | mutex_lock(&cfg80211_drv_mutex); | ||
1683 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL); | ||
1684 | mutex_unlock(&cfg80211_drv_mutex); | ||
1685 | return r; | ||
1686 | } | ||
1687 | |||
1688 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | ||
1689 | { | ||
1690 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | ||
1691 | struct nlattr *nl_reg_rule; | ||
1692 | char *alpha2 = NULL; | ||
1693 | int rem_reg_rules = 0, r = 0; | ||
1694 | u32 num_rules = 0, rule_idx = 0, size_of_regd; | ||
1695 | struct ieee80211_regdomain *rd = NULL; | ||
1696 | |||
1697 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | ||
1698 | return -EINVAL; | ||
1699 | |||
1700 | if (!info->attrs[NL80211_ATTR_REG_RULES]) | ||
1701 | return -EINVAL; | ||
1702 | |||
1703 | alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | ||
1704 | |||
1705 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | ||
1706 | rem_reg_rules) { | ||
1707 | num_rules++; | ||
1708 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | ||
1709 | goto bad_reg; | ||
1710 | } | ||
1711 | |||
1712 | if (!reg_is_valid_request(alpha2)) | ||
1713 | return -EINVAL; | ||
1714 | |||
1715 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
1716 | (num_rules * sizeof(struct ieee80211_reg_rule)); | ||
1717 | |||
1718 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
1719 | if (!rd) | ||
1720 | return -ENOMEM; | ||
1721 | |||
1722 | rd->n_reg_rules = num_rules; | ||
1723 | rd->alpha2[0] = alpha2[0]; | ||
1724 | rd->alpha2[1] = alpha2[1]; | ||
1725 | |||
1726 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | ||
1727 | rem_reg_rules) { | ||
1728 | nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, | ||
1729 | nla_data(nl_reg_rule), nla_len(nl_reg_rule), | ||
1730 | reg_rule_policy); | ||
1731 | r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); | ||
1732 | if (r) | ||
1733 | goto bad_reg; | ||
1734 | |||
1735 | rule_idx++; | ||
1736 | |||
1737 | if (rule_idx > NL80211_MAX_SUPP_REG_RULES) | ||
1738 | goto bad_reg; | ||
1739 | } | ||
1740 | |||
1741 | BUG_ON(rule_idx != num_rules); | ||
1742 | |||
1743 | mutex_lock(&cfg80211_drv_mutex); | ||
1744 | r = set_regdom(rd); | ||
1745 | mutex_unlock(&cfg80211_drv_mutex); | ||
1746 | if (r) | ||
1747 | goto bad_reg; | ||
1748 | |||
1749 | return r; | ||
1750 | |||
1751 | bad_reg: | ||
1752 | kfree(rd); | ||
1753 | return -EINVAL; | ||
1754 | } | ||
1755 | |||
1528 | static struct genl_ops nl80211_ops[] = { | 1756 | static struct genl_ops nl80211_ops[] = { |
1529 | { | 1757 | { |
1530 | .cmd = NL80211_CMD_GET_WIPHY, | 1758 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -1656,6 +1884,24 @@ static struct genl_ops nl80211_ops[] = { | |||
1656 | .policy = nl80211_policy, | 1884 | .policy = nl80211_policy, |
1657 | .flags = GENL_ADMIN_PERM, | 1885 | .flags = GENL_ADMIN_PERM, |
1658 | }, | 1886 | }, |
1887 | { | ||
1888 | .cmd = NL80211_CMD_SET_BSS, | ||
1889 | .doit = nl80211_set_bss, | ||
1890 | .policy = nl80211_policy, | ||
1891 | .flags = GENL_ADMIN_PERM, | ||
1892 | }, | ||
1893 | { | ||
1894 | .cmd = NL80211_CMD_SET_REG, | ||
1895 | .doit = nl80211_set_reg, | ||
1896 | .policy = nl80211_policy, | ||
1897 | .flags = GENL_ADMIN_PERM, | ||
1898 | }, | ||
1899 | { | ||
1900 | .cmd = NL80211_CMD_REQ_SET_REG, | ||
1901 | .doit = nl80211_req_set_reg, | ||
1902 | .policy = nl80211_policy, | ||
1903 | .flags = GENL_ADMIN_PERM, | ||
1904 | }, | ||
1659 | }; | 1905 | }; |
1660 | 1906 | ||
1661 | /* multicast groups */ | 1907 | /* multicast groups */ |