diff options
Diffstat (limited to 'net/wireless/nl80211.c')
-rw-r--r-- | net/wireless/nl80211.c | 438 |
1 files changed, 416 insertions, 22 deletions
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c index 5b3474798b8d..64a7460af734 100644 --- a/net/wireless/nl80211.c +++ b/net/wireless/nl80211.c | |||
@@ -81,8 +81,12 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = { | |||
81 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, | 81 | [NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 }, |
82 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, | 82 | [NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY, |
83 | .len = NL80211_MAX_SUPP_RATES }, | 83 | .len = NL80211_MAX_SUPP_RATES }, |
84 | [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 }, | ||
84 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, | 85 | [NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 }, |
85 | [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED }, | 86 | [NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED }, |
87 | [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY, | ||
88 | .len = IEEE80211_MAX_MESH_ID_LEN }, | ||
89 | [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 }, | ||
86 | }; | 90 | }; |
87 | 91 | ||
88 | /* message building helper */ | 92 | /* message building helper */ |
@@ -369,11 +373,14 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags) | |||
369 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | 373 | static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) |
370 | { | 374 | { |
371 | struct cfg80211_registered_device *drv; | 375 | struct cfg80211_registered_device *drv; |
376 | struct vif_params params; | ||
372 | int err, ifindex; | 377 | int err, ifindex; |
373 | enum nl80211_iftype type; | 378 | enum nl80211_iftype type; |
374 | struct net_device *dev; | 379 | struct net_device *dev; |
375 | u32 flags; | 380 | u32 flags; |
376 | 381 | ||
382 | memset(¶ms, 0, sizeof(params)); | ||
383 | |||
377 | if (info->attrs[NL80211_ATTR_IFTYPE]) { | 384 | if (info->attrs[NL80211_ATTR_IFTYPE]) { |
378 | type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); | 385 | type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]); |
379 | if (type > NL80211_IFTYPE_MAX) | 386 | if (type > NL80211_IFTYPE_MAX) |
@@ -392,12 +399,18 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
392 | goto unlock; | 399 | goto unlock; |
393 | } | 400 | } |
394 | 401 | ||
402 | if (type == NL80211_IFTYPE_MESH_POINT && | ||
403 | info->attrs[NL80211_ATTR_MESH_ID]) { | ||
404 | params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); | ||
405 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); | ||
406 | } | ||
407 | |||
395 | rtnl_lock(); | 408 | rtnl_lock(); |
396 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | 409 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
397 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | 410 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, |
398 | &flags); | 411 | &flags); |
399 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, | 412 | err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex, |
400 | type, err ? NULL : &flags); | 413 | type, err ? NULL : &flags, ¶ms); |
401 | rtnl_unlock(); | 414 | rtnl_unlock(); |
402 | 415 | ||
403 | unlock: | 416 | unlock: |
@@ -408,10 +421,13 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info) | |||
408 | static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | 421 | static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) |
409 | { | 422 | { |
410 | struct cfg80211_registered_device *drv; | 423 | struct cfg80211_registered_device *drv; |
424 | struct vif_params params; | ||
411 | int err; | 425 | int err; |
412 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; | 426 | enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED; |
413 | u32 flags; | 427 | u32 flags; |
414 | 428 | ||
429 | memset(¶ms, 0, sizeof(params)); | ||
430 | |||
415 | if (!info->attrs[NL80211_ATTR_IFNAME]) | 431 | if (!info->attrs[NL80211_ATTR_IFNAME]) |
416 | return -EINVAL; | 432 | return -EINVAL; |
417 | 433 | ||
@@ -430,15 +446,22 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info) | |||
430 | goto unlock; | 446 | goto unlock; |
431 | } | 447 | } |
432 | 448 | ||
449 | if (type == NL80211_IFTYPE_MESH_POINT && | ||
450 | info->attrs[NL80211_ATTR_MESH_ID]) { | ||
451 | params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]); | ||
452 | params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]); | ||
453 | } | ||
454 | |||
433 | rtnl_lock(); | 455 | rtnl_lock(); |
434 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? | 456 | err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ? |
435 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, | 457 | info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL, |
436 | &flags); | 458 | &flags); |
437 | err = drv->ops->add_virtual_intf(&drv->wiphy, | 459 | err = drv->ops->add_virtual_intf(&drv->wiphy, |
438 | nla_data(info->attrs[NL80211_ATTR_IFNAME]), | 460 | nla_data(info->attrs[NL80211_ATTR_IFNAME]), |
439 | type, err ? NULL : &flags); | 461 | type, err ? NULL : &flags, ¶ms); |
440 | rtnl_unlock(); | 462 | rtnl_unlock(); |
441 | 463 | ||
464 | |||
442 | unlock: | 465 | unlock: |
443 | cfg80211_put_dev(drv); | 466 | cfg80211_put_dev(drv); |
444 | return err; | 467 | return err; |
@@ -866,10 +889,10 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags) | |||
866 | 889 | ||
867 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | 890 | static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, |
868 | int flags, struct net_device *dev, | 891 | int flags, struct net_device *dev, |
869 | u8 *mac_addr, struct station_stats *stats) | 892 | u8 *mac_addr, struct station_info *sinfo) |
870 | { | 893 | { |
871 | void *hdr; | 894 | void *hdr; |
872 | struct nlattr *statsattr; | 895 | struct nlattr *sinfoattr; |
873 | 896 | ||
874 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | 897 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); |
875 | if (!hdr) | 898 | if (!hdr) |
@@ -878,20 +901,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
878 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | 901 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); |
879 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); | 902 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr); |
880 | 903 | ||
881 | statsattr = nla_nest_start(msg, NL80211_ATTR_STA_STATS); | 904 | sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO); |
882 | if (!statsattr) | 905 | if (!sinfoattr) |
883 | goto nla_put_failure; | 906 | goto nla_put_failure; |
884 | if (stats->filled & STATION_STAT_INACTIVE_TIME) | 907 | if (sinfo->filled & STATION_INFO_INACTIVE_TIME) |
885 | NLA_PUT_U32(msg, NL80211_STA_STAT_INACTIVE_TIME, | 908 | NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME, |
886 | stats->inactive_time); | 909 | sinfo->inactive_time); |
887 | if (stats->filled & STATION_STAT_RX_BYTES) | 910 | if (sinfo->filled & STATION_INFO_RX_BYTES) |
888 | NLA_PUT_U32(msg, NL80211_STA_STAT_RX_BYTES, | 911 | NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES, |
889 | stats->rx_bytes); | 912 | sinfo->rx_bytes); |
890 | if (stats->filled & STATION_STAT_TX_BYTES) | 913 | if (sinfo->filled & STATION_INFO_TX_BYTES) |
891 | NLA_PUT_U32(msg, NL80211_STA_STAT_TX_BYTES, | 914 | NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES, |
892 | stats->tx_bytes); | 915 | sinfo->tx_bytes); |
893 | 916 | if (sinfo->filled & STATION_INFO_LLID) | |
894 | nla_nest_end(msg, statsattr); | 917 | NLA_PUT_U16(msg, NL80211_STA_INFO_LLID, |
918 | sinfo->llid); | ||
919 | if (sinfo->filled & STATION_INFO_PLID) | ||
920 | NLA_PUT_U16(msg, NL80211_STA_INFO_PLID, | ||
921 | sinfo->plid); | ||
922 | if (sinfo->filled & STATION_INFO_PLINK_STATE) | ||
923 | NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE, | ||
924 | sinfo->plink_state); | ||
925 | |||
926 | nla_nest_end(msg, sinfoattr); | ||
895 | 927 | ||
896 | return genlmsg_end(msg, hdr); | 928 | return genlmsg_end(msg, hdr); |
897 | 929 | ||
@@ -899,17 +931,80 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq, | |||
899 | return genlmsg_cancel(msg, hdr); | 931 | return genlmsg_cancel(msg, hdr); |
900 | } | 932 | } |
901 | 933 | ||
934 | static int nl80211_dump_station(struct sk_buff *skb, | ||
935 | struct netlink_callback *cb) | ||
936 | { | ||
937 | int wp_idx = 0; | ||
938 | int if_idx = 0; | ||
939 | int sta_idx = cb->args[2]; | ||
940 | int wp_start = cb->args[0]; | ||
941 | int if_start = cb->args[1]; | ||
942 | struct station_info sinfo; | ||
943 | struct cfg80211_registered_device *dev; | ||
944 | struct wireless_dev *wdev; | ||
945 | u8 mac_addr[ETH_ALEN]; | ||
946 | int err; | ||
947 | int exit = 0; | ||
948 | |||
949 | /* TODO: filter by device */ | ||
950 | mutex_lock(&cfg80211_drv_mutex); | ||
951 | list_for_each_entry(dev, &cfg80211_drv_list, list) { | ||
952 | if (exit) | ||
953 | break; | ||
954 | if (++wp_idx < wp_start) | ||
955 | continue; | ||
956 | if_idx = 0; | ||
957 | |||
958 | mutex_lock(&dev->devlist_mtx); | ||
959 | list_for_each_entry(wdev, &dev->netdev_list, list) { | ||
960 | if (exit) | ||
961 | break; | ||
962 | if (++if_idx < if_start) | ||
963 | continue; | ||
964 | if (!dev->ops->dump_station) | ||
965 | continue; | ||
966 | |||
967 | for (;; ++sta_idx) { | ||
968 | rtnl_lock(); | ||
969 | err = dev->ops->dump_station(&dev->wiphy, | ||
970 | wdev->netdev, sta_idx, mac_addr, | ||
971 | &sinfo); | ||
972 | rtnl_unlock(); | ||
973 | if (err) { | ||
974 | sta_idx = 0; | ||
975 | break; | ||
976 | } | ||
977 | if (nl80211_send_station(skb, | ||
978 | NETLINK_CB(cb->skb).pid, | ||
979 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
980 | wdev->netdev, mac_addr, | ||
981 | &sinfo) < 0) { | ||
982 | exit = 1; | ||
983 | break; | ||
984 | } | ||
985 | } | ||
986 | } | ||
987 | mutex_unlock(&dev->devlist_mtx); | ||
988 | } | ||
989 | mutex_unlock(&cfg80211_drv_mutex); | ||
990 | |||
991 | cb->args[0] = wp_idx; | ||
992 | cb->args[1] = if_idx; | ||
993 | cb->args[2] = sta_idx; | ||
994 | |||
995 | return skb->len; | ||
996 | } | ||
902 | 997 | ||
903 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | 998 | static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) |
904 | { | 999 | { |
905 | struct cfg80211_registered_device *drv; | 1000 | struct cfg80211_registered_device *drv; |
906 | int err; | 1001 | int err; |
907 | struct net_device *dev; | 1002 | struct net_device *dev; |
908 | struct station_stats stats; | 1003 | struct station_info sinfo; |
909 | struct sk_buff *msg; | 1004 | struct sk_buff *msg; |
910 | u8 *mac_addr = NULL; | 1005 | u8 *mac_addr = NULL; |
911 | 1006 | ||
912 | memset(&stats, 0, sizeof(stats)); | 1007 | memset(&sinfo, 0, sizeof(sinfo)); |
913 | 1008 | ||
914 | if (!info->attrs[NL80211_ATTR_MAC]) | 1009 | if (!info->attrs[NL80211_ATTR_MAC]) |
915 | return -EINVAL; | 1010 | return -EINVAL; |
@@ -926,15 +1021,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info) | |||
926 | } | 1021 | } |
927 | 1022 | ||
928 | rtnl_lock(); | 1023 | rtnl_lock(); |
929 | err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &stats); | 1024 | err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo); |
930 | rtnl_unlock(); | 1025 | rtnl_unlock(); |
931 | 1026 | ||
1027 | if (err) | ||
1028 | goto out; | ||
1029 | |||
932 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | 1030 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); |
933 | if (!msg) | 1031 | if (!msg) |
934 | goto out; | 1032 | goto out; |
935 | 1033 | ||
936 | if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, | 1034 | if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0, |
937 | dev, mac_addr, &stats) < 0) | 1035 | dev, mac_addr, &sinfo) < 0) |
938 | goto out_free; | 1036 | goto out_free; |
939 | 1037 | ||
940 | err = genlmsg_unicast(msg, info->snd_pid); | 1038 | err = genlmsg_unicast(msg, info->snd_pid); |
@@ -1005,6 +1103,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info) | |||
1005 | ¶ms.station_flags)) | 1103 | ¶ms.station_flags)) |
1006 | return -EINVAL; | 1104 | return -EINVAL; |
1007 | 1105 | ||
1106 | if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION]) | ||
1107 | params.plink_action = | ||
1108 | nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]); | ||
1109 | |||
1008 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | 1110 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); |
1009 | if (err) | 1111 | if (err) |
1010 | return err; | 1112 | return err; |
@@ -1119,6 +1221,273 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info) | |||
1119 | return err; | 1221 | return err; |
1120 | } | 1222 | } |
1121 | 1223 | ||
1224 | static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq, | ||
1225 | int flags, struct net_device *dev, | ||
1226 | u8 *dst, u8 *next_hop, | ||
1227 | struct mpath_info *pinfo) | ||
1228 | { | ||
1229 | void *hdr; | ||
1230 | struct nlattr *pinfoattr; | ||
1231 | |||
1232 | hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION); | ||
1233 | if (!hdr) | ||
1234 | return -1; | ||
1235 | |||
1236 | NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex); | ||
1237 | NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst); | ||
1238 | NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop); | ||
1239 | |||
1240 | pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO); | ||
1241 | if (!pinfoattr) | ||
1242 | goto nla_put_failure; | ||
1243 | if (pinfo->filled & MPATH_INFO_FRAME_QLEN) | ||
1244 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN, | ||
1245 | pinfo->frame_qlen); | ||
1246 | if (pinfo->filled & MPATH_INFO_DSN) | ||
1247 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN, | ||
1248 | pinfo->dsn); | ||
1249 | if (pinfo->filled & MPATH_INFO_METRIC) | ||
1250 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC, | ||
1251 | pinfo->metric); | ||
1252 | if (pinfo->filled & MPATH_INFO_EXPTIME) | ||
1253 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME, | ||
1254 | pinfo->exptime); | ||
1255 | if (pinfo->filled & MPATH_INFO_FLAGS) | ||
1256 | NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS, | ||
1257 | pinfo->flags); | ||
1258 | if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT) | ||
1259 | NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT, | ||
1260 | pinfo->discovery_timeout); | ||
1261 | if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES) | ||
1262 | NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES, | ||
1263 | pinfo->discovery_retries); | ||
1264 | |||
1265 | nla_nest_end(msg, pinfoattr); | ||
1266 | |||
1267 | return genlmsg_end(msg, hdr); | ||
1268 | |||
1269 | nla_put_failure: | ||
1270 | return genlmsg_cancel(msg, hdr); | ||
1271 | } | ||
1272 | |||
1273 | static int nl80211_dump_mpath(struct sk_buff *skb, | ||
1274 | struct netlink_callback *cb) | ||
1275 | { | ||
1276 | int wp_idx = 0; | ||
1277 | int if_idx = 0; | ||
1278 | int sta_idx = cb->args[2]; | ||
1279 | int wp_start = cb->args[0]; | ||
1280 | int if_start = cb->args[1]; | ||
1281 | struct mpath_info pinfo; | ||
1282 | struct cfg80211_registered_device *dev; | ||
1283 | struct wireless_dev *wdev; | ||
1284 | u8 dst[ETH_ALEN]; | ||
1285 | u8 next_hop[ETH_ALEN]; | ||
1286 | int err; | ||
1287 | int exit = 0; | ||
1288 | |||
1289 | /* TODO: filter by device */ | ||
1290 | mutex_lock(&cfg80211_drv_mutex); | ||
1291 | list_for_each_entry(dev, &cfg80211_drv_list, list) { | ||
1292 | if (exit) | ||
1293 | break; | ||
1294 | if (++wp_idx < wp_start) | ||
1295 | continue; | ||
1296 | if_idx = 0; | ||
1297 | |||
1298 | mutex_lock(&dev->devlist_mtx); | ||
1299 | list_for_each_entry(wdev, &dev->netdev_list, list) { | ||
1300 | if (exit) | ||
1301 | break; | ||
1302 | if (++if_idx < if_start) | ||
1303 | continue; | ||
1304 | if (!dev->ops->dump_mpath) | ||
1305 | continue; | ||
1306 | |||
1307 | for (;; ++sta_idx) { | ||
1308 | rtnl_lock(); | ||
1309 | err = dev->ops->dump_mpath(&dev->wiphy, | ||
1310 | wdev->netdev, sta_idx, dst, | ||
1311 | next_hop, &pinfo); | ||
1312 | rtnl_unlock(); | ||
1313 | if (err) { | ||
1314 | sta_idx = 0; | ||
1315 | break; | ||
1316 | } | ||
1317 | if (nl80211_send_mpath(skb, | ||
1318 | NETLINK_CB(cb->skb).pid, | ||
1319 | cb->nlh->nlmsg_seq, NLM_F_MULTI, | ||
1320 | wdev->netdev, dst, next_hop, | ||
1321 | &pinfo) < 0) { | ||
1322 | exit = 1; | ||
1323 | break; | ||
1324 | } | ||
1325 | } | ||
1326 | } | ||
1327 | mutex_unlock(&dev->devlist_mtx); | ||
1328 | } | ||
1329 | mutex_unlock(&cfg80211_drv_mutex); | ||
1330 | |||
1331 | cb->args[0] = wp_idx; | ||
1332 | cb->args[1] = if_idx; | ||
1333 | cb->args[2] = sta_idx; | ||
1334 | |||
1335 | return skb->len; | ||
1336 | } | ||
1337 | |||
1338 | static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1339 | { | ||
1340 | struct cfg80211_registered_device *drv; | ||
1341 | int err; | ||
1342 | struct net_device *dev; | ||
1343 | struct mpath_info pinfo; | ||
1344 | struct sk_buff *msg; | ||
1345 | u8 *dst = NULL; | ||
1346 | u8 next_hop[ETH_ALEN]; | ||
1347 | |||
1348 | memset(&pinfo, 0, sizeof(pinfo)); | ||
1349 | |||
1350 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
1351 | return -EINVAL; | ||
1352 | |||
1353 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1354 | |||
1355 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1356 | if (err) | ||
1357 | return err; | ||
1358 | |||
1359 | if (!drv->ops->get_mpath) { | ||
1360 | err = -EOPNOTSUPP; | ||
1361 | goto out; | ||
1362 | } | ||
1363 | |||
1364 | rtnl_lock(); | ||
1365 | err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo); | ||
1366 | rtnl_unlock(); | ||
1367 | |||
1368 | if (err) | ||
1369 | goto out; | ||
1370 | |||
1371 | msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL); | ||
1372 | if (!msg) | ||
1373 | goto out; | ||
1374 | |||
1375 | if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0, | ||
1376 | dev, dst, next_hop, &pinfo) < 0) | ||
1377 | goto out_free; | ||
1378 | |||
1379 | err = genlmsg_unicast(msg, info->snd_pid); | ||
1380 | goto out; | ||
1381 | |||
1382 | out_free: | ||
1383 | nlmsg_free(msg); | ||
1384 | |||
1385 | out: | ||
1386 | cfg80211_put_dev(drv); | ||
1387 | dev_put(dev); | ||
1388 | return err; | ||
1389 | } | ||
1390 | |||
1391 | static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1392 | { | ||
1393 | struct cfg80211_registered_device *drv; | ||
1394 | int err; | ||
1395 | struct net_device *dev; | ||
1396 | u8 *dst = NULL; | ||
1397 | u8 *next_hop = NULL; | ||
1398 | |||
1399 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
1400 | return -EINVAL; | ||
1401 | |||
1402 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) | ||
1403 | return -EINVAL; | ||
1404 | |||
1405 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1406 | next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); | ||
1407 | |||
1408 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1409 | if (err) | ||
1410 | return err; | ||
1411 | |||
1412 | if (!drv->ops->change_mpath) { | ||
1413 | err = -EOPNOTSUPP; | ||
1414 | goto out; | ||
1415 | } | ||
1416 | |||
1417 | rtnl_lock(); | ||
1418 | err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop); | ||
1419 | rtnl_unlock(); | ||
1420 | |||
1421 | out: | ||
1422 | cfg80211_put_dev(drv); | ||
1423 | dev_put(dev); | ||
1424 | return err; | ||
1425 | } | ||
1426 | static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1427 | { | ||
1428 | struct cfg80211_registered_device *drv; | ||
1429 | int err; | ||
1430 | struct net_device *dev; | ||
1431 | u8 *dst = NULL; | ||
1432 | u8 *next_hop = NULL; | ||
1433 | |||
1434 | if (!info->attrs[NL80211_ATTR_MAC]) | ||
1435 | return -EINVAL; | ||
1436 | |||
1437 | if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]) | ||
1438 | return -EINVAL; | ||
1439 | |||
1440 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1441 | next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]); | ||
1442 | |||
1443 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1444 | if (err) | ||
1445 | return err; | ||
1446 | |||
1447 | if (!drv->ops->add_mpath) { | ||
1448 | err = -EOPNOTSUPP; | ||
1449 | goto out; | ||
1450 | } | ||
1451 | |||
1452 | rtnl_lock(); | ||
1453 | err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop); | ||
1454 | rtnl_unlock(); | ||
1455 | |||
1456 | out: | ||
1457 | cfg80211_put_dev(drv); | ||
1458 | dev_put(dev); | ||
1459 | return err; | ||
1460 | } | ||
1461 | |||
1462 | static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info) | ||
1463 | { | ||
1464 | struct cfg80211_registered_device *drv; | ||
1465 | int err; | ||
1466 | struct net_device *dev; | ||
1467 | u8 *dst = NULL; | ||
1468 | |||
1469 | if (info->attrs[NL80211_ATTR_MAC]) | ||
1470 | dst = nla_data(info->attrs[NL80211_ATTR_MAC]); | ||
1471 | |||
1472 | err = get_drv_dev_by_info_ifindex(info, &drv, &dev); | ||
1473 | if (err) | ||
1474 | return err; | ||
1475 | |||
1476 | if (!drv->ops->del_mpath) { | ||
1477 | err = -EOPNOTSUPP; | ||
1478 | goto out; | ||
1479 | } | ||
1480 | |||
1481 | rtnl_lock(); | ||
1482 | err = drv->ops->del_mpath(&drv->wiphy, dev, dst); | ||
1483 | rtnl_unlock(); | ||
1484 | |||
1485 | out: | ||
1486 | cfg80211_put_dev(drv); | ||
1487 | dev_put(dev); | ||
1488 | return err; | ||
1489 | } | ||
1490 | |||
1122 | static struct genl_ops nl80211_ops[] = { | 1491 | static struct genl_ops nl80211_ops[] = { |
1123 | { | 1492 | { |
1124 | .cmd = NL80211_CMD_GET_WIPHY, | 1493 | .cmd = NL80211_CMD_GET_WIPHY, |
@@ -1203,7 +1572,7 @@ static struct genl_ops nl80211_ops[] = { | |||
1203 | { | 1572 | { |
1204 | .cmd = NL80211_CMD_GET_STATION, | 1573 | .cmd = NL80211_CMD_GET_STATION, |
1205 | .doit = nl80211_get_station, | 1574 | .doit = nl80211_get_station, |
1206 | /* TODO: implement dumpit */ | 1575 | .dumpit = nl80211_dump_station, |
1207 | .policy = nl80211_policy, | 1576 | .policy = nl80211_policy, |
1208 | .flags = GENL_ADMIN_PERM, | 1577 | .flags = GENL_ADMIN_PERM, |
1209 | }, | 1578 | }, |
@@ -1225,6 +1594,31 @@ static struct genl_ops nl80211_ops[] = { | |||
1225 | .policy = nl80211_policy, | 1594 | .policy = nl80211_policy, |
1226 | .flags = GENL_ADMIN_PERM, | 1595 | .flags = GENL_ADMIN_PERM, |
1227 | }, | 1596 | }, |
1597 | { | ||
1598 | .cmd = NL80211_CMD_GET_MPATH, | ||
1599 | .doit = nl80211_get_mpath, | ||
1600 | .dumpit = nl80211_dump_mpath, | ||
1601 | .policy = nl80211_policy, | ||
1602 | .flags = GENL_ADMIN_PERM, | ||
1603 | }, | ||
1604 | { | ||
1605 | .cmd = NL80211_CMD_SET_MPATH, | ||
1606 | .doit = nl80211_set_mpath, | ||
1607 | .policy = nl80211_policy, | ||
1608 | .flags = GENL_ADMIN_PERM, | ||
1609 | }, | ||
1610 | { | ||
1611 | .cmd = NL80211_CMD_NEW_MPATH, | ||
1612 | .doit = nl80211_new_mpath, | ||
1613 | .policy = nl80211_policy, | ||
1614 | .flags = GENL_ADMIN_PERM, | ||
1615 | }, | ||
1616 | { | ||
1617 | .cmd = NL80211_CMD_DEL_MPATH, | ||
1618 | .doit = nl80211_del_mpath, | ||
1619 | .policy = nl80211_policy, | ||
1620 | .flags = GENL_ADMIN_PERM, | ||
1621 | }, | ||
1228 | }; | 1622 | }; |
1229 | 1623 | ||
1230 | /* multicast groups */ | 1624 | /* multicast groups */ |