diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 417 |
1 files changed, 405 insertions, 12 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 572793c8c7ab..1e728fff474e 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -58,6 +58,9 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
58 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, | 58 | [NL80211_ATTR_WIPHY] = { .type = NLA_U32 }, |
59 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, | 59 | [NL80211_ATTR_WIPHY_NAME] = { .type = NLA_NUL_STRING, |
60 | .len = BUS_ID_SIZE-1 }, | 60 | .len = BUS_ID_SIZE-1 }, |
61 | [NL80211_ATTR_WIPHY_TXQ_PARAMS] = { .type = NLA_NESTED }, | ||
62 | [NL80211_ATTR_WIPHY_FREQ] = { .type = NLA_U32 }, | ||
63 | [NL80211_ATTR_WIPHY_CHANNEL_TYPE] = { .type = NLA_U32 }, | ||
61 | 64 | ||
62 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, | 65 | [NL80211_ATTR_IFTYPE] = { .type = NLA_U32 }, |
63 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, | 66 | [NL80211_ATTR_IFINDEX] = { .type = NLA_U32 }, |
@@ -84,7 +87,7 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
84 | .len = NL80211_MAX_SUPP_RATES }, | 87 | .len = NL80211_MAX_SUPP_RATES }, |
85 | [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, | 88 | [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, |
86 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | 89 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, |
87 | [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED }, | 90 | [NL80211_ATTR_MNTR_FLAGS] = { /* NLA_NESTED can't be empty */ }, |
88 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, | 91 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, |
89 | .len = IEEE80211_MAX_MESH_ID_LEN }, | 92 | .len = IEEE80211_MAX_MESH_ID_LEN }, |
90 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, | 93 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, |
@@ -95,6 +98,10 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
95 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, | 98 | [NL80211_ATTR_BSS_CTS_PROT] = { .type = NLA_U8 }, |
96 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, | 99 | [NL80211_ATTR_BSS_SHORT_PREAMBLE] = { .type = NLA_U8 }, |
97 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, | 100 | [NL80211_ATTR_BSS_SHORT_SLOT_TIME] = { .type = NLA_U8 }, |
101 | [NL80211_ATTR_BSS_BASIC_RATES] = { .type = NLA_BINARY, | ||
102 | .len = NL80211_MAX_SUPP_RATES }, | ||
103 | |||
104 | [NL80211_ATTR_MESH_PARAMS] = { .type = NLA_NESTED }, | ||
98 | 105 | ||
99 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, | 106 | [NL80211_ATTR_HT_CAPABILITY] = { .type = NLA_BINARY, |
100 | .len = NL80211_HT_CAPABILITY_LEN }, | 107 | .len = NL80211_HT_CAPABILITY_LEN }, |
@@ -157,6 +164,19 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
157 | if (!nl_band) | 164 | if (!nl_band) |
158 | goto nla_put_failure; | 165 | goto nla_put_failure; |
159 | 166 | ||
167 | /* add HT info */ | ||
168 | if (dev->wiphy.bands[band]->ht_cap.ht_supported) { | ||
169 | NLA_PUT(msg, NL80211_BAND_ATTR_HT_MCS_SET, | ||
170 | sizeof(dev->wiphy.bands[band]->ht_cap.mcs), | ||
171 | &dev->wiphy.bands[band]->ht_cap.mcs); | ||
172 | NLA_PUT_U16(msg, NL80211_BAND_ATTR_HT_CAPA, | ||
173 | dev->wiphy.bands[band]->ht_cap.cap); | ||
174 | NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_FACTOR, | ||
175 | dev->wiphy.bands[band]->ht_cap.ampdu_factor); | ||
176 | NLA_PUT_U8(msg, NL80211_BAND_ATTR_HT_AMPDU_DENSITY, | ||
177 | dev->wiphy.bands[band]->ht_cap.ampdu_density); | ||
178 | } | ||
179 | |||
160 | /* add frequencies */ | 180 | /* add frequencies */ |
161 | nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); | 181 | nl_freqs = nla_nest_start(msg, NL80211_BAND_ATTR_FREQS); |
162 | if (!nl_freqs) | 182 | if (!nl_freqs) |
@@ -180,6 +200,9 @@ static int nl80211_send_wiphy(struct sk_buff *msg, u32 pid, u32 seq, int flags, | |||
180 | if (chan->flags & IEEE80211_CHAN_RADAR) | 200 | if (chan->flags & IEEE80211_CHAN_RADAR) |
181 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); | 201 | NLA_PUT_FLAG(msg, NL80211_FREQUENCY_ATTR_RADAR); |
182 | 202 | ||
203 | NLA_PUT_U32(msg, NL80211_FREQUENCY_ATTR_MAX_TX_POWER, | ||
204 | DBM_TO_MBM(chan->max_power)); | ||
205 | |||
183 | nla_nest_end(msg, nl_freq); | 206 | nla_nest_end(msg, nl_freq); |
184 | } | 207 | } |
185 | 208 | ||
@@ -269,20 +292,142 @@ static int nl80211_get_wiphy(struct sk_buff *skb, struct genl_info *info) | |||
269 | return -ENOBUFS; | 292 | return -ENOBUFS; |
270 | } | 293 | } |
271 | 294 | ||
295 | static const struct nla_policy txq_params_policy[NL80211_TXQ_ATTR_MAX + 1] = { | ||
296 | [NL80211_TXQ_ATTR_QUEUE] = { .type = NLA_U8 }, | ||
297 | [NL80211_TXQ_ATTR_TXOP] = { .type = NLA_U16 }, | ||
298 | [NL80211_TXQ_ATTR_CWMIN] = { .type = NLA_U16 }, | ||
299 | [NL80211_TXQ_ATTR_CWMAX] = { .type = NLA_U16 }, | ||
300 | [NL80211_TXQ_ATTR_AIFS] = { .type = NLA_U8 }, | ||
301 | }; | ||
302 | |||
303 | static int parse_txq_params(struct nlattr *tb[], | ||
304 | struct ieee80211_txq_params *txq_params) | ||
305 | { | ||
306 | if (!tb[NL80211_TXQ_ATTR_QUEUE] || !tb[NL80211_TXQ_ATTR_TXOP] || | ||
307 | !tb[NL80211_TXQ_ATTR_CWMIN] || !tb[NL80211_TXQ_ATTR_CWMAX] || | ||
308 | !tb[NL80211_TXQ_ATTR_AIFS]) | ||
309 | return -EINVAL; | ||
310 | |||
311 | txq_params->queue = nla_get_u8(tb[NL80211_TXQ_ATTR_QUEUE]); | ||
312 | txq_params->txop = nla_get_u16(tb[NL80211_TXQ_ATTR_TXOP]); | ||
313 | txq_params->cwmin = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMIN]); | ||
314 | txq_params->cwmax = nla_get_u16(tb[NL80211_TXQ_ATTR_CWMAX]); | ||
315 | txq_params->aifs = nla_get_u8(tb[NL80211_TXQ_ATTR_AIFS]); | ||
316 | |||
317 | return 0; | ||
318 | } | ||
319 | |||
272 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) | 320 | static int nl80211_set_wiphy(struct sk_buff *skb, struct genl_info *info) |
273 | { | 321 | { |
274 | struct cfg80211_registered_device *rdev; | 322 | struct cfg80211_registered_device *rdev; |
275 | int result; | 323 | int result = 0, rem_txq_params = 0; |
276 | 324 | struct nlattr *nl_txq_params; | |
277 | if (!info->attrs[NL80211_ATTR_WIPHY_NAME]) | ||
278 | return -EINVAL; | ||
279 | 325 | ||
280 | rdev = cfg80211_get_dev_from_info(info); | 326 | rdev = cfg80211_get_dev_from_info(info); |
281 | if (IS_ERR(rdev)) | 327 | if (IS_ERR(rdev)) |
282 | return PTR_ERR(rdev); | 328 | return PTR_ERR(rdev); |
283 | 329 | ||
284 | result = cfg80211_dev_rename(rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); | 330 | if (info->attrs[NL80211_ATTR_WIPHY_NAME]) { |
331 | result = cfg80211_dev_rename( | ||
332 | rdev, nla_data(info->attrs[NL80211_ATTR_WIPHY_NAME])); | ||
333 | if (result) | ||
334 | goto bad_res; | ||
335 | } | ||
336 | |||
337 | if (info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS]) { | ||
338 | struct ieee80211_txq_params txq_params; | ||
339 | struct nlattr *tb[NL80211_TXQ_ATTR_MAX + 1]; | ||
340 | |||
341 | if (!rdev->ops->set_txq_params) { | ||
342 | result = -EOPNOTSUPP; | ||
343 | goto bad_res; | ||
344 | } | ||
345 | |||
346 | nla_for_each_nested(nl_txq_params, | ||
347 | info->attrs[NL80211_ATTR_WIPHY_TXQ_PARAMS], | ||
348 | rem_txq_params) { | ||
349 | nla_parse(tb, NL80211_TXQ_ATTR_MAX, | ||
350 | nla_data(nl_txq_params), | ||
351 | nla_len(nl_txq_params), | ||
352 | txq_params_policy); | ||
353 | result = parse_txq_params(tb, &txq_params); | ||
354 | if (result) | ||
355 | goto bad_res; | ||
356 | |||
357 | result = rdev->ops->set_txq_params(&rdev->wiphy, | ||
358 | &txq_params); | ||
359 | if (result) | ||
360 | goto bad_res; | ||
361 | } | ||
362 | } | ||
363 | |||
364 | if (info->attrs[NL80211_ATTR_WIPHY_FREQ]) { | ||
365 | enum nl80211_channel_type channel_type = NL80211_CHAN_NO_HT; | ||
366 | struct ieee80211_channel *chan; | ||
367 | struct ieee80211_sta_ht_cap *ht_cap; | ||
368 | u32 freq, sec_freq; | ||
369 | |||
370 | if (!rdev->ops->set_channel) { | ||
371 | result = -EOPNOTSUPP; | ||
372 | goto bad_res; | ||
373 | } | ||
374 | |||
375 | result = -EINVAL; | ||
376 | |||
377 | if (info->attrs[NL80211_ATTR_WIPHY_CHANNEL_TYPE]) { | ||
378 | channel_type = nla_get_u32(info->attrs[ | ||
379 | NL80211_ATTR_WIPHY_CHANNEL_TYPE]); | ||
380 | if (channel_type != NL80211_CHAN_NO_HT && | ||
381 | channel_type != NL80211_CHAN_HT20 && | ||
382 | channel_type != NL80211_CHAN_HT40PLUS && | ||
383 | channel_type != NL80211_CHAN_HT40MINUS) | ||
384 | goto bad_res; | ||
385 | } | ||
386 | |||
387 | freq = nla_get_u32(info->attrs[NL80211_ATTR_WIPHY_FREQ]); | ||
388 | chan = ieee80211_get_channel(&rdev->wiphy, freq); | ||
389 | |||
390 | /* Primary channel not allowed */ | ||
391 | if (!chan || chan->flags & IEEE80211_CHAN_DISABLED) | ||
392 | goto bad_res; | ||
393 | |||
394 | if (channel_type == NL80211_CHAN_HT40MINUS) | ||
395 | sec_freq = freq - 20; | ||
396 | else if (channel_type == NL80211_CHAN_HT40PLUS) | ||
397 | sec_freq = freq + 20; | ||
398 | else | ||
399 | sec_freq = 0; | ||
400 | |||
401 | ht_cap = &rdev->wiphy.bands[chan->band]->ht_cap; | ||
285 | 402 | ||
403 | /* no HT capabilities */ | ||
404 | if (channel_type != NL80211_CHAN_NO_HT && | ||
405 | !ht_cap->ht_supported) | ||
406 | goto bad_res; | ||
407 | |||
408 | if (sec_freq) { | ||
409 | struct ieee80211_channel *schan; | ||
410 | |||
411 | /* no 40 MHz capabilities */ | ||
412 | if (!(ht_cap->cap & IEEE80211_HT_CAP_SUP_WIDTH_20_40) || | ||
413 | (ht_cap->cap & IEEE80211_HT_CAP_40MHZ_INTOLERANT)) | ||
414 | goto bad_res; | ||
415 | |||
416 | schan = ieee80211_get_channel(&rdev->wiphy, sec_freq); | ||
417 | |||
418 | /* Secondary channel not allowed */ | ||
419 | if (!schan || schan->flags & IEEE80211_CHAN_DISABLED) | ||
420 | goto bad_res; | ||
421 | } | ||
422 | |||
423 | result = rdev->ops->set_channel(&rdev->wiphy, chan, | ||
424 | channel_type); | ||
425 | if (result) | ||
426 | goto bad_res; | ||
427 | } | ||
428 | |||
429 | |||
430 | bad_res: | ||
286 | cfg80211_put_dev(rdev); | 431 | cfg80211_put_dev(rdev); |
287 | return result; | 432 | return result; |
288 | } | 433 | } |
@@ -945,12 +1090,46 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags) | |||
945 | return 0; | 1090 | return 0; |
946 | } | 1091 | } |
947 | 1092 | ||
1093 | static u16 nl80211_calculate_bitrate(struct rate_info *rate) | ||
1094 | { | ||
1095 | int modulation, streams, bitrate; | ||
1096 | |||
1097 | if (!(rate->flags & RATE_INFO_FLAGS_MCS)) | ||
1098 | return rate->legacy; | ||
1099 | |||
1100 | /* the formula below does only work for MCS values smaller than 32 */ | ||
1101 | if (rate->mcs >= 32) | ||
1102 | return 0; | ||
1103 | |||
1104 | modulation = rate->mcs & 7; | ||
1105 | streams = (rate->mcs >> 3) + 1; | ||
1106 | |||
1107 | bitrate = (rate->flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) ? | ||
1108 | 13500000 : 6500000; | ||
1109 | |||
1110 | if (modulation < 4) | ||
1111 | bitrate *= (modulation + 1); | ||
1112 | else if (modulation == 4) | ||
1113 | bitrate *= (modulation + 2); | ||
1114 | else | ||
1115 | bitrate *= (modulation + 3); | ||
1116 | |||
1117 | bitrate *= streams; | ||
1118 | |||
1119 | if (rate->flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1120 | bitrate = (bitrate / 9) * 10; | ||
1121 | |||
1122 | /* do NOT round down here */ | ||
1123 | return (bitrate + 50000) / 100000; | ||
1124 | } | ||
1125 | |||
948 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 1126 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
949 | int flags, struct net_device *dev, | 1127 | int flags, struct net_device *dev, |
950 | u8 *mac_addr, struct station_info *sinfo) | 1128 | u8 *mac_addr, struct station_info *sinfo) |
951 | { | 1129 | { |
952 | void *hdr; | 1130 | void *hdr; |
953 | struct nlattr *sinfoattr; | 1131 | struct nlattr *sinfoattr, *txrate; |
1132 | u16 bitrate; | ||
954 | 1133 | ||
955 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | 1134 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); |
956 | if (!hdr) | 1135 | if (!hdr) |
@@ -980,7 +1159,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
980 | if (sinfo->filled & STATION_INFO_PLINK_STATE) | 1159 | if (sinfo->filled & STATION_INFO_PLINK_STATE) |
981 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, | 1160 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, |
982 | sinfo->plink_state); | 1161 | sinfo->plink_state); |
1162 | if (sinfo->filled & STATION_INFO_SIGNAL) | ||
1163 | NLA_PUT_U8(msg, NL80211_STA_INFO_SIGNAL, | ||
1164 | sinfo->signal); | ||
1165 | if (sinfo->filled & STATION_INFO_TX_BITRATE) { | ||
1166 | txrate = nla_nest_start(msg, NL80211_STA_INFO_TX_BITRATE); | ||
1167 | if (!txrate) | ||
1168 | goto nla_put_failure; | ||
1169 | |||
1170 | /* nl80211_calculate_bitrate will return 0 for mcs >= 32 */ | ||
1171 | bitrate = nl80211_calculate_bitrate(&sinfo->txrate); | ||
1172 | if (bitrate > 0) | ||
1173 | NLA_PUT_U16(msg, NL80211_RATE_INFO_BITRATE, bitrate); | ||
1174 | |||
1175 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_MCS) | ||
1176 | NLA_PUT_U8(msg, NL80211_RATE_INFO_MCS, | ||
1177 | sinfo->txrate.mcs); | ||
1178 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_40_MHZ_WIDTH) | ||
1179 | NLA_PUT_FLAG(msg, NL80211_RATE_INFO_40_MHZ_WIDTH); | ||
1180 | if (sinfo->txrate.flags & RATE_INFO_FLAGS_SHORT_GI) | ||
1181 | NLA_PUT_FLAG(msg, NL80211_RATE_INFO_SHORT_GI); | ||
983 | 1182 | ||
1183 | nla_nest_end(msg, txrate); | ||
1184 | } | ||
984 | nla_nest_end(msg, sinfoattr); | 1185 | nla_nest_end(msg, sinfoattr); |
985 | 1186 | ||
986 | return genlmsg_end(msg, hdr); | 1187 | return genlmsg_end(msg, hdr); |
@@ -1598,6 +1799,12 @@ static int nl80211_set_bss(struct sk_buff *skb, struct genl_info *info) | |||
1598 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) | 1799 | if (info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]) |
1599 | params.use_short_slot_time = | 1800 | params.use_short_slot_time = |
1600 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); | 1801 | nla_get_u8(info->attrs[NL80211_ATTR_BSS_SHORT_SLOT_TIME]); |
1802 | if (info->attrs[NL80211_ATTR_BSS_BASIC_RATES]) { | ||
1803 | params.basic_rates = | ||
1804 | nla_data(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | ||
1805 | params.basic_rates_len = | ||
1806 | nla_len(info->attrs[NL80211_ATTR_BSS_BASIC_RATES]); | ||
1807 | } | ||
1601 | 1808 | ||
1602 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | 1809 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); |
1603 | if (err) | 1810 | if (err) |
@@ -1680,11 +1887,188 @@ static int nl80211_req_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1680 | return -EINVAL; | 1887 | return -EINVAL; |
1681 | #endif | 1888 | #endif |
1682 | mutex_lock(&cfg80211_drv_mutex); | 1889 | mutex_lock(&cfg80211_drv_mutex); |
1683 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, NULL); | 1890 | r = __regulatory_hint(NULL, REGDOM_SET_BY_USER, data, 0, ENVIRON_ANY); |
1684 | mutex_unlock(&cfg80211_drv_mutex); | 1891 | mutex_unlock(&cfg80211_drv_mutex); |
1685 | return r; | 1892 | return r; |
1686 | } | 1893 | } |
1687 | 1894 | ||
1895 | static int nl80211_get_mesh_params(struct sk_buff *skb, | ||
1896 | struct genl_info *info) | ||
1897 | { | ||
1898 | struct cfg80211_registered_device *drv; | ||
1899 | struct mesh_config cur_params; | ||
1900 | int err; | ||
1901 | struct net_device *dev; | ||
1902 | void *hdr; | ||
1903 | struct nlattr *pinfoattr; | ||
1904 | struct sk_buff *msg; | ||
1905 | |||
1906 | /* Look up our device */ | ||
1907 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
1908 | if (err) | ||
1909 | return err; | ||
1910 | |||
1911 | /* Get the mesh params */ | ||
1912 | rtnl_lock(); | ||
1913 | err = drv->ops->get_mesh_params(&drv->wiphy, dev, &cur_params); | ||
1914 | rtnl_unlock(); | ||
1915 | if (err) | ||
1916 | goto out; | ||
1917 | |||
1918 | /* Draw up a netlink message to send back */ | ||
1919 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
1920 | if (!msg) { | ||
1921 | err = -ENOBUFS; | ||
1922 | goto out; | ||
1923 | } | ||
1924 | hdr = nl80211hdr_put(msg, info->snd_pid, info->snd_seq, 0, | ||
1925 | NL80211_CMD_GET_MESH_PARAMS); | ||
1926 | if (!hdr) | ||
1927 | goto nla_put_failure; | ||
1928 | pinfoattr = nla_nest_start(msg, NL80211_ATTR_MESH_PARAMS); | ||
1929 | if (!pinfoattr) | ||
1930 | goto nla_put_failure; | ||
1931 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
1932 | NLA_PUT_U16(msg, NL80211_MESHCONF_RETRY_TIMEOUT, | ||
1933 | cur_params.dot11MeshRetryTimeout); | ||
1934 | NLA_PUT_U16(msg, NL80211_MESHCONF_CONFIRM_TIMEOUT, | ||
1935 | cur_params.dot11MeshConfirmTimeout); | ||
1936 | NLA_PUT_U16(msg, NL80211_MESHCONF_HOLDING_TIMEOUT, | ||
1937 | cur_params.dot11MeshHoldingTimeout); | ||
1938 | NLA_PUT_U16(msg, NL80211_MESHCONF_MAX_PEER_LINKS, | ||
1939 | cur_params.dot11MeshMaxPeerLinks); | ||
1940 | NLA_PUT_U8(msg, NL80211_MESHCONF_MAX_RETRIES, | ||
1941 | cur_params.dot11MeshMaxRetries); | ||
1942 | NLA_PUT_U8(msg, NL80211_MESHCONF_TTL, | ||
1943 | cur_params.dot11MeshTTL); | ||
1944 | NLA_PUT_U8(msg, NL80211_MESHCONF_AUTO_OPEN_PLINKS, | ||
1945 | cur_params.auto_open_plinks); | ||
1946 | NLA_PUT_U8(msg, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | ||
1947 | cur_params.dot11MeshHWMPmaxPREQretries); | ||
1948 | NLA_PUT_U32(msg, NL80211_MESHCONF_PATH_REFRESH_TIME, | ||
1949 | cur_params.path_refresh_time); | ||
1950 | NLA_PUT_U16(msg, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | ||
1951 | cur_params.min_discovery_timeout); | ||
1952 | NLA_PUT_U32(msg, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | ||
1953 | cur_params.dot11MeshHWMPactivePathTimeout); | ||
1954 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | ||
1955 | cur_params.dot11MeshHWMPpreqMinInterval); | ||
1956 | NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | ||
1957 | cur_params.dot11MeshHWMPnetDiameterTraversalTime); | ||
1958 | nla_nest_end(msg, pinfoattr); | ||
1959 | genlmsg_end(msg, hdr); | ||
1960 | err = genlmsg_unicast(msg, info->snd_pid); | ||
1961 | goto out; | ||
1962 | |||
1963 | nla_put_failure: | ||
1964 | genlmsg_cancel(msg, hdr); | ||
1965 | err = -EMSGSIZE; | ||
1966 | out: | ||
1967 | /* Cleanup */ | ||
1968 | cfg80211_put_dev(drv); | ||
1969 | dev_put(dev); | ||
1970 | return err; | ||
1971 | } | ||
1972 | |||
1973 | #define FILL_IN_MESH_PARAM_IF_SET(table, cfg, param, mask, attr_num, nla_fn) \ | ||
1974 | do {\ | ||
1975 | if (table[attr_num]) {\ | ||
1976 | cfg.param = nla_fn(table[attr_num]); \ | ||
1977 | mask |= (1 << (attr_num - 1)); \ | ||
1978 | } \ | ||
1979 | } while (0);\ | ||
1980 | |||
1981 | static struct nla_policy | ||
1982 | nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = { | ||
1983 | [NL80211_MESHCONF_RETRY_TIMEOUT] = { .type = NLA_U16 }, | ||
1984 | [NL80211_MESHCONF_CONFIRM_TIMEOUT] = { .type = NLA_U16 }, | ||
1985 | [NL80211_MESHCONF_HOLDING_TIMEOUT] = { .type = NLA_U16 }, | ||
1986 | [NL80211_MESHCONF_MAX_PEER_LINKS] = { .type = NLA_U16 }, | ||
1987 | [NL80211_MESHCONF_MAX_RETRIES] = { .type = NLA_U8 }, | ||
1988 | [NL80211_MESHCONF_TTL] = { .type = NLA_U8 }, | ||
1989 | [NL80211_MESHCONF_AUTO_OPEN_PLINKS] = { .type = NLA_U8 }, | ||
1990 | |||
1991 | [NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES] = { .type = NLA_U8 }, | ||
1992 | [NL80211_MESHCONF_PATH_REFRESH_TIME] = { .type = NLA_U32 }, | ||
1993 | [NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT] = { .type = NLA_U16 }, | ||
1994 | [NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 }, | ||
1995 | [NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 }, | ||
1996 | [NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 }, | ||
1997 | }; | ||
1998 | |||
1999 | static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info) | ||
2000 | { | ||
2001 | int err; | ||
2002 | u32 mask; | ||
2003 | struct cfg80211_registered_device *drv; | ||
2004 | struct net_device *dev; | ||
2005 | struct mesh_config cfg; | ||
2006 | struct nlattr *tb[NL80211_MESHCONF_ATTR_MAX + 1]; | ||
2007 | struct nlattr *parent_attr; | ||
2008 | |||
2009 | parent_attr = info->attrs[NL80211_ATTR_MESH_PARAMS]; | ||
2010 | if (!parent_attr) | ||
2011 | return -EINVAL; | ||
2012 | if (nla_parse_nested(tb, NL80211_MESHCONF_ATTR_MAX, | ||
2013 | parent_attr, nl80211_meshconf_params_policy)) | ||
2014 | return -EINVAL; | ||
2015 | |||
2016 | err = get_drv_dev_by_info_ifindex(info->attrs, &drv, &dev); | ||
2017 | if (err) | ||
2018 | return err; | ||
2019 | |||
2020 | /* This makes sure that there aren't more than 32 mesh config | ||
2021 | * parameters (otherwise our bitfield scheme would not work.) */ | ||
2022 | BUILD_BUG_ON(NL80211_MESHCONF_ATTR_MAX > 32); | ||
2023 | |||
2024 | /* Fill in the params struct */ | ||
2025 | mask = 0; | ||
2026 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshRetryTimeout, | ||
2027 | mask, NL80211_MESHCONF_RETRY_TIMEOUT, nla_get_u16); | ||
2028 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshConfirmTimeout, | ||
2029 | mask, NL80211_MESHCONF_CONFIRM_TIMEOUT, nla_get_u16); | ||
2030 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHoldingTimeout, | ||
2031 | mask, NL80211_MESHCONF_HOLDING_TIMEOUT, nla_get_u16); | ||
2032 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxPeerLinks, | ||
2033 | mask, NL80211_MESHCONF_MAX_PEER_LINKS, nla_get_u16); | ||
2034 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshMaxRetries, | ||
2035 | mask, NL80211_MESHCONF_MAX_RETRIES, nla_get_u8); | ||
2036 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshTTL, | ||
2037 | mask, NL80211_MESHCONF_TTL, nla_get_u8); | ||
2038 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, auto_open_plinks, | ||
2039 | mask, NL80211_MESHCONF_AUTO_OPEN_PLINKS, nla_get_u8); | ||
2040 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPmaxPREQretries, | ||
2041 | mask, NL80211_MESHCONF_HWMP_MAX_PREQ_RETRIES, | ||
2042 | nla_get_u8); | ||
2043 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, path_refresh_time, | ||
2044 | mask, NL80211_MESHCONF_PATH_REFRESH_TIME, nla_get_u32); | ||
2045 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, min_discovery_timeout, | ||
2046 | mask, NL80211_MESHCONF_MIN_DISCOVERY_TIMEOUT, | ||
2047 | nla_get_u16); | ||
2048 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPactivePathTimeout, | ||
2049 | mask, NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT, | ||
2050 | nla_get_u32); | ||
2051 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshHWMPpreqMinInterval, | ||
2052 | mask, NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL, | ||
2053 | nla_get_u16); | ||
2054 | FILL_IN_MESH_PARAM_IF_SET(tb, cfg, | ||
2055 | dot11MeshHWMPnetDiameterTraversalTime, | ||
2056 | mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME, | ||
2057 | nla_get_u16); | ||
2058 | |||
2059 | /* Apply changes */ | ||
2060 | rtnl_lock(); | ||
2061 | err = drv->ops->set_mesh_params(&drv->wiphy, dev, &cfg, mask); | ||
2062 | rtnl_unlock(); | ||
2063 | |||
2064 | /* cleanup */ | ||
2065 | cfg80211_put_dev(drv); | ||
2066 | dev_put(dev); | ||
2067 | return err; | ||
2068 | } | ||
2069 | |||
2070 | #undef FILL_IN_MESH_PARAM_IF_SET | ||
2071 | |||
1688 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | 2072 | static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) |
1689 | { | 2073 | { |
1690 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; | 2074 | struct nlattr *tb[NL80211_REG_RULE_ATTR_MAX + 1]; |
@@ -1743,12 +2127,9 @@ static int nl80211_set_reg(struct sk_buff *skb, struct genl_info *info) | |||
1743 | mutex_lock(&cfg80211_drv_mutex); | 2127 | mutex_lock(&cfg80211_drv_mutex); |
1744 | r = set_regdom(rd); | 2128 | r = set_regdom(rd); |
1745 | mutex_unlock(&cfg80211_drv_mutex); | 2129 | mutex_unlock(&cfg80211_drv_mutex); |
1746 | if (r) | ||
1747 | goto bad_reg; | ||
1748 | |||
1749 | return r; | 2130 | return r; |
1750 | 2131 | ||
1751 | bad_reg: | 2132 | bad_reg: |
1752 | kfree(rd); | 2133 | kfree(rd); |
1753 | return -EINVAL; | 2134 | return -EINVAL; |
1754 | } | 2135 | } |
@@ -1902,6 +2283,18 @@ static struct genl_ops nl80211_ops[] = { | |||
1902 | .policy = nl80211_policy, | 2283 | .policy = nl80211_policy, |
1903 | .flags = GENL_ADMIN_PERM, | 2284 | .flags = GENL_ADMIN_PERM, |
1904 | }, | 2285 | }, |
2286 | { | ||
2287 | .cmd = NL80211_CMD_GET_MESH_PARAMS, | ||
2288 | .doit = nl80211_get_mesh_params, | ||
2289 | .policy = nl80211_policy, | ||
2290 | /* can be retrieved by unprivileged users */ | ||
2291 | }, | ||
2292 | { | ||
2293 | .cmd = NL80211_CMD_SET_MESH_PARAMS, | ||
2294 | .doit = nl80211_set_mesh_params, | ||
2295 | .policy = nl80211_policy, | ||
2296 | .flags = GENL_ADMIN_PERM, | ||
2297 | }, | ||
1905 | }; | 2298 | }; |
1906 | 2299 | ||
1907 | /* multicast groups */ | 2300 | /* multicast groups */ |