diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 151 |
1 files changed, 151 insertions, 0 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 77880ba8b619..1221d726ed50 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 = { |
@@ -88,6 +89,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
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 }, |
90 | 91 | ||
92 | [NL80211_ATTR_REG_ALPHA2] = { .type = NLA_STRING, .len = 2 }, | ||
93 | [NL80211_ATTR_REG_RULES] = { .type = NLA_NESTED }, | ||
94 | |||
91 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, | 95 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, |
92 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, | 96 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, |
93 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, | 97 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, |
@@ -1599,6 +1603,141 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
1599 | return err; | 1603 | return err; |
1600 | } | 1604 | } |
1601 | 1605 | ||
1606 | static const struct nla_policy | ||
1607 | reg_rule_policy[NL80211_REG_RULE_ATTR_MAX + 1] = { | ||
1608 | [NL80211_ATTR_REG_RULE_FLAGS] = { .type = NLA_U32 }, | ||
1609 | [NL80211_ATTR_FREQ_RANGE_START] = { .type = NLA_U32 }, | ||
1610 | [NL80211_ATTR_FREQ_RANGE_END] = { .type = NLA_U32 }, | ||
1611 | [NL80211_ATTR_FREQ_RANGE_MAX_BW] = { .type = NLA_U32 }, | ||
1612 | [NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN] = { .type = NLA_U32 }, | ||
1613 | [NL80211_ATTR_POWER_RULE_MAX_EIRP] = { .type = NLA_U32 }, | ||
1614 | }; | ||
1615 | |||
1616 | static int parse_reg_rule(struct nlattr *tb[], | ||
1617 | struct ieee80211_reg_rule *reg_rule) | ||
1618 | { | ||
1619 | struct ieee80211_freq_range *freq_range = ®_rule->freq_range; | ||
1620 | struct ieee80211_power_rule *power_rule = ®_rule->power_rule; | ||
1621 | |||
1622 | if (!tb[NL80211_ATTR_REG_RULE_FLAGS]) | ||
1623 | return -EINVAL; | ||
1624 | if (!tb[NL80211_ATTR_FREQ_RANGE_START]) | ||
1625 | return -EINVAL; | ||
1626 | if (!tb[NL80211_ATTR_FREQ_RANGE_END]) | ||
1627 | return -EINVAL; | ||
1628 | if (!tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]) | ||
1629 | return -EINVAL; | ||
1630 | if (!tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]) | ||
1631 | return -EINVAL; | ||
1632 | |||
1633 | reg_rule->flags = nla_get_u32(tb[NL80211_ATTR_REG_RULE_FLAGS]); | ||
1634 | |||
1635 | freq_range->start_freq_khz = | ||
1636 | nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_START]); | ||
1637 | freq_range->end_freq_khz = | ||
1638 | nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_END]); | ||
1639 | freq_range->max_bandwidth_khz = | ||
1640 | nla_get_u32(tb[NL80211_ATTR_FREQ_RANGE_MAX_BW]); | ||
1641 | |||
1642 | power_rule->max_eirp = | ||
1643 | nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_EIRP]); | ||
1644 | |||
1645 | if (tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]) | ||
1646 | power_rule->max_antenna_gain = | ||
1647 | nla_get_u32(tb[NL80211_ATTR_POWER_RULE_MAX_ANT_GAIN]); | ||
1648 | |||
1649 | return 0; | ||
1650 | } | ||
1651 | |||
1652 | static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | ||
1653 | { | ||
1654 | int r; | ||
1655 | char *data = NULL; | ||
1656 | |||
1657 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | ||
1658 | return -EINVAL; | ||
1659 | |||
1660 | data = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | ||
1661 | |||
1662 | #ifdef CONFIG_WIRELESS_OLD_REGULATORY | ||
1663 | /* We ignore world regdom requests with the old regdom setup */ | ||
1664 | if (is_world_regdom(data)) | ||
1665 | return -EINVAL; | ||
1666 | #endif | ||
1667 | mutex_lock(&cfg80211_drv_mutex); | ||
1668 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL); | ||
1669 | mutex_unlock(&cfg80211_drv_mutex); | ||
1670 | return r; | ||
1671 | } | ||
1672 | |||
1673 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | ||
1674 | { | ||
1675 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | ||
1676 | struct nlattr *nl_reg_rule; | ||
1677 | char *alpha2 = NULL; | ||
1678 | int rem_reg_rules = 0, r = 0; | ||
1679 | u32 num_rules = 0, rule_idx = 0, size_of_regd; | ||
1680 | struct ieee80211_regdomain *rd = NULL; | ||
1681 | |||
1682 | if (!info->attrs[NL80211_ATTR_REG_ALPHA2]) | ||
1683 | return -EINVAL; | ||
1684 | |||
1685 | if (!info->attrs[NL80211_ATTR_REG_RULES]) | ||
1686 | return -EINVAL; | ||
1687 | |||
1688 | alpha2 = nla_data(info->attrs[NL80211_ATTR_REG_ALPHA2]); | ||
1689 | |||
1690 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | ||
1691 | rem_reg_rules) { | ||
1692 | num_rules++; | ||
1693 | if (num_rules > NL80211_MAX_SUPP_REG_RULES) | ||
1694 | goto bad_reg; | ||
1695 | } | ||
1696 | |||
1697 | if (!reg_is_valid_request(alpha2)) | ||
1698 | return -EINVAL; | ||
1699 | |||
1700 | size_of_regd = sizeof(struct ieee80211_regdomain) + | ||
1701 | (num_rules * sizeof(struct ieee80211_reg_rule)); | ||
1702 | |||
1703 | rd = kzalloc(size_of_regd, GFP_KERNEL); | ||
1704 | if (!rd) | ||
1705 | return -ENOMEM; | ||
1706 | |||
1707 | rd->n_reg_rules = num_rules; | ||
1708 | rd->alpha2[0] = alpha2[0]; | ||
1709 | rd->alpha2[1] = alpha2[1]; | ||
1710 | |||
1711 | nla_for_each_nested(nl_reg_rule, info->attrs[NL80211_ATTR_REG_RULES], | ||
1712 | rem_reg_rules) { | ||
1713 | nla_parse(tb, NL80211_REG_RULE_ATTR_MAX, | ||
1714 | nla_data(nl_reg_rule), nla_len(nl_reg_rule), | ||
1715 | reg_rule_policy); | ||
1716 | r = parse_reg_rule(tb, &rd->reg_rules[rule_idx]); | ||
1717 | if (r) | ||
1718 | goto bad_reg; | ||
1719 | |||
1720 | rule_idx++; | ||
1721 | |||
1722 | if (rule_idx > NL80211_MAX_SUPP_REG_RULES) | ||
1723 | goto bad_reg; | ||
1724 | } | ||
1725 | |||
1726 | BUG_ON(rule_idx != num_rules); | ||
1727 | |||
1728 | mutex_lock(&cfg80211_drv_mutex); | ||
1729 | r = set_regdom(rd); | ||
1730 | mutex_unlock(&cfg80211_drv_mutex); | ||
1731 | if (r) | ||
1732 | goto bad_reg; | ||
1733 | |||
1734 | return r; | ||
1735 | |||
1736 | bad_reg: | ||
1737 | kfree(rd); | ||
1738 | return -EINVAL; | ||
1739 | } | ||
1740 | |||
1602 | static struct genl_ops nl80211_ops[] = { | 1741 | static struct genl_ops nl80211_ops[] = { |
1603 | { | 1742 | { |
1604 | .cmd = NL80211_CMD_GET_WIPHY, | 1743 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -1736,6 +1875,18 @@ static struct genl_ops nl80211_ops[] = { | |||
1736 | .policy = nl80211_policy, | 1875 | .policy = nl80211_policy, |
1737 | .flags = GENL_ADMIN_PERM, | 1876 | .flags = GENL_ADMIN_PERM, |
1738 | }, | 1877 | }, |
1878 | { | ||
1879 | .cmd = NL80211_CMD_SET_REG, | ||
1880 | .doit = nl80211_set_reg, | ||
1881 | .policy = nl80211_policy, | ||
1882 | .flags = GENL_ADMIN_PERM, | ||
1883 | }, | ||
1884 | { | ||
1885 | .cmd = NL80211_CMD_REQ_SET_REG, | ||
1886 | .doit = nl80211_req_set_reg, | ||
1887 | .policy = nl80211_policy, | ||
1888 | .flags = GENL_ADMIN_PERM, | ||
1889 | }, | ||
1739 | }; | 1890 | }; |
1740 | 1891 | ||
1741 | /* multicast groups */ | 1892 | /* multicast groups */ |