|
@@ -81,8 +81,12 @@ static struct nla_policy nl80211_policy[NL80211_ATTR_MAX+1] __read_mostly = {
|
|
[NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
|
|
[NL80211_ATTR_STA_LISTEN_INTERVAL] = { .type = NLA_U16 },
|
|
[NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
|
|
[NL80211_ATTR_STA_SUPPORTED_RATES] = { .type = NLA_BINARY,
|
|
.len = NL80211_MAX_SUPP_RATES },
|
|
.len = NL80211_MAX_SUPP_RATES },
|
|
|
|
+ [NL80211_ATTR_STA_PLINK_ACTION] = { .type = NLA_U8 },
|
|
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_STA_VLAN] = { .type = NLA_U32 },
|
|
[NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
|
|
[NL80211_ATTR_MNTR_FLAGS] = { .type = NLA_NESTED },
|
|
|
|
+ [NL80211_ATTR_MESH_ID] = { .type = NLA_BINARY,
|
|
|
|
+ .len = IEEE80211_MAX_MESH_ID_LEN },
|
|
|
|
+ [NL80211_ATTR_MPATH_NEXT_HOP] = { .type = NLA_U32 },
|
|
};
|
|
};
|
|
|
|
|
|
/* message building helper */
|
|
/* message building helper */
|
|
@@ -369,11 +373,14 @@ static int parse_monitor_flags(struct nlattr *nla, u32 *mntrflags)
|
|
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *drv;
|
|
struct cfg80211_registered_device *drv;
|
|
|
|
+ struct vif_params params;
|
|
int err, ifindex;
|
|
int err, ifindex;
|
|
enum nl80211_iftype type;
|
|
enum nl80211_iftype type;
|
|
struct net_device *dev;
|
|
struct net_device *dev;
|
|
u32 flags;
|
|
u32 flags;
|
|
|
|
|
|
|
|
+ memset(¶ms, 0, sizeof(params));
|
|
|
|
+
|
|
if (info->attrs[NL80211_ATTR_IFTYPE]) {
|
|
if (info->attrs[NL80211_ATTR_IFTYPE]) {
|
|
type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
|
|
type = nla_get_u32(info->attrs[NL80211_ATTR_IFTYPE]);
|
|
if (type > NL80211_IFTYPE_MAX)
|
|
if (type > NL80211_IFTYPE_MAX)
|
|
@@ -392,12 +399,18 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (type == NL80211_IFTYPE_MESH_POINT &&
|
|
|
|
+ info->attrs[NL80211_ATTR_MESH_ID]) {
|
|
|
|
+ params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
+ params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
+ }
|
|
|
|
+
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
|
|
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
|
|
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
|
|
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
|
|
&flags);
|
|
&flags);
|
|
err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
|
|
err = drv->ops->change_virtual_intf(&drv->wiphy, ifindex,
|
|
- type, err ? NULL : &flags);
|
|
|
|
|
|
+ type, err ? NULL : &flags, ¶ms);
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
|
|
|
|
unlock:
|
|
unlock:
|
|
@@ -408,10 +421,13 @@ static int nl80211_set_interface(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *drv;
|
|
struct cfg80211_registered_device *drv;
|
|
|
|
+ struct vif_params params;
|
|
int err;
|
|
int err;
|
|
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
|
enum nl80211_iftype type = NL80211_IFTYPE_UNSPECIFIED;
|
|
u32 flags;
|
|
u32 flags;
|
|
|
|
|
|
|
|
+ memset(¶ms, 0, sizeof(params));
|
|
|
|
+
|
|
if (!info->attrs[NL80211_ATTR_IFNAME])
|
|
if (!info->attrs[NL80211_ATTR_IFNAME])
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
@@ -430,15 +446,22 @@ static int nl80211_new_interface(struct sk_buff *skb, struct genl_info *info)
|
|
goto unlock;
|
|
goto unlock;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (type == NL80211_IFTYPE_MESH_POINT &&
|
|
|
|
+ info->attrs[NL80211_ATTR_MESH_ID]) {
|
|
|
|
+ params.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
+ params.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
|
|
|
|
+ }
|
|
|
|
+
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
|
|
err = parse_monitor_flags(type == NL80211_IFTYPE_MONITOR ?
|
|
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
|
|
info->attrs[NL80211_ATTR_MNTR_FLAGS] : NULL,
|
|
&flags);
|
|
&flags);
|
|
err = drv->ops->add_virtual_intf(&drv->wiphy,
|
|
err = drv->ops->add_virtual_intf(&drv->wiphy,
|
|
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
|
nla_data(info->attrs[NL80211_ATTR_IFNAME]),
|
|
- type, err ? NULL : &flags);
|
|
|
|
|
|
+ type, err ? NULL : &flags, ¶ms);
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
+
|
|
unlock:
|
|
unlock:
|
|
cfg80211_put_dev(drv);
|
|
cfg80211_put_dev(drv);
|
|
return err;
|
|
return err;
|
|
@@ -866,10 +889,10 @@ static int parse_station_flags(struct nlattr *nla, u32 *staflags)
|
|
|
|
|
|
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
|
static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
|
int flags, struct net_device *dev,
|
|
int flags, struct net_device *dev,
|
|
- u8 *mac_addr, struct station_stats *stats)
|
|
|
|
|
|
+ u8 *mac_addr, struct station_info *sinfo)
|
|
{
|
|
{
|
|
void *hdr;
|
|
void *hdr;
|
|
- struct nlattr *statsattr;
|
|
|
|
|
|
+ struct nlattr *sinfoattr;
|
|
|
|
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
if (!hdr)
|
|
if (!hdr)
|
|
@@ -878,20 +901,29 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
|
|
NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, mac_addr);
|
|
|
|
|
|
- statsattr = nla_nest_start(msg, NL80211_ATTR_STA_STATS);
|
|
|
|
- if (!statsattr)
|
|
|
|
|
|
+ sinfoattr = nla_nest_start(msg, NL80211_ATTR_STA_INFO);
|
|
|
|
+ if (!sinfoattr)
|
|
goto nla_put_failure;
|
|
goto nla_put_failure;
|
|
- if (stats->filled & STATION_STAT_INACTIVE_TIME)
|
|
|
|
- NLA_PUT_U32(msg, NL80211_STA_STAT_INACTIVE_TIME,
|
|
|
|
- stats->inactive_time);
|
|
|
|
- if (stats->filled & STATION_STAT_RX_BYTES)
|
|
|
|
- NLA_PUT_U32(msg, NL80211_STA_STAT_RX_BYTES,
|
|
|
|
- stats->rx_bytes);
|
|
|
|
- if (stats->filled & STATION_STAT_TX_BYTES)
|
|
|
|
- NLA_PUT_U32(msg, NL80211_STA_STAT_TX_BYTES,
|
|
|
|
- stats->tx_bytes);
|
|
|
|
-
|
|
|
|
- nla_nest_end(msg, statsattr);
|
|
|
|
|
|
+ if (sinfo->filled & STATION_INFO_INACTIVE_TIME)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_STA_INFO_INACTIVE_TIME,
|
|
|
|
+ sinfo->inactive_time);
|
|
|
|
+ if (sinfo->filled & STATION_INFO_RX_BYTES)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_STA_INFO_RX_BYTES,
|
|
|
|
+ sinfo->rx_bytes);
|
|
|
|
+ if (sinfo->filled & STATION_INFO_TX_BYTES)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_STA_INFO_TX_BYTES,
|
|
|
|
+ sinfo->tx_bytes);
|
|
|
|
+ if (sinfo->filled & STATION_INFO_LLID)
|
|
|
|
+ NLA_PUT_U16(msg, NL80211_STA_INFO_LLID,
|
|
|
|
+ sinfo->llid);
|
|
|
|
+ if (sinfo->filled & STATION_INFO_PLID)
|
|
|
|
+ NLA_PUT_U16(msg, NL80211_STA_INFO_PLID,
|
|
|
|
+ sinfo->plid);
|
|
|
|
+ if (sinfo->filled & STATION_INFO_PLINK_STATE)
|
|
|
|
+ NLA_PUT_U8(msg, NL80211_STA_INFO_PLINK_STATE,
|
|
|
|
+ sinfo->plink_state);
|
|
|
|
+
|
|
|
|
+ nla_nest_end(msg, sinfoattr);
|
|
|
|
|
|
return genlmsg_end(msg, hdr);
|
|
return genlmsg_end(msg, hdr);
|
|
|
|
|
|
@@ -899,17 +931,80 @@ static int nl80211_send_station(struct sk_buff *msg, u32 pid, u32 seq,
|
|
return genlmsg_cancel(msg, hdr);
|
|
return genlmsg_cancel(msg, hdr);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_dump_station(struct sk_buff *skb,
|
|
|
|
+ struct netlink_callback *cb)
|
|
|
|
+{
|
|
|
|
+ int wp_idx = 0;
|
|
|
|
+ int if_idx = 0;
|
|
|
|
+ int sta_idx = cb->args[2];
|
|
|
|
+ int wp_start = cb->args[0];
|
|
|
|
+ int if_start = cb->args[1];
|
|
|
|
+ struct station_info sinfo;
|
|
|
|
+ struct cfg80211_registered_device *dev;
|
|
|
|
+ struct wireless_dev *wdev;
|
|
|
|
+ u8 mac_addr[ETH_ALEN];
|
|
|
|
+ int err;
|
|
|
|
+ int exit = 0;
|
|
|
|
+
|
|
|
|
+ /* TODO: filter by device */
|
|
|
|
+ mutex_lock(&cfg80211_drv_mutex);
|
|
|
|
+ list_for_each_entry(dev, &cfg80211_drv_list, list) {
|
|
|
|
+ if (exit)
|
|
|
|
+ break;
|
|
|
|
+ if (++wp_idx < wp_start)
|
|
|
|
+ continue;
|
|
|
|
+ if_idx = 0;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&dev->devlist_mtx);
|
|
|
|
+ list_for_each_entry(wdev, &dev->netdev_list, list) {
|
|
|
|
+ if (exit)
|
|
|
|
+ break;
|
|
|
|
+ if (++if_idx < if_start)
|
|
|
|
+ continue;
|
|
|
|
+ if (!dev->ops->dump_station)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ for (;; ++sta_idx) {
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ err = dev->ops->dump_station(&dev->wiphy,
|
|
|
|
+ wdev->netdev, sta_idx, mac_addr,
|
|
|
|
+ &sinfo);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ if (err) {
|
|
|
|
+ sta_idx = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (nl80211_send_station(skb,
|
|
|
|
+ NETLINK_CB(cb->skb).pid,
|
|
|
|
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
+ wdev->netdev, mac_addr,
|
|
|
|
+ &sinfo) < 0) {
|
|
|
|
+ exit = 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&dev->devlist_mtx);
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&cfg80211_drv_mutex);
|
|
|
|
+
|
|
|
|
+ cb->args[0] = wp_idx;
|
|
|
|
+ cb->args[1] = if_idx;
|
|
|
|
+ cb->args[2] = sta_idx;
|
|
|
|
+
|
|
|
|
+ return skb->len;
|
|
|
|
+}
|
|
|
|
|
|
static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
{
|
|
{
|
|
struct cfg80211_registered_device *drv;
|
|
struct cfg80211_registered_device *drv;
|
|
int err;
|
|
int err;
|
|
struct net_device *dev;
|
|
struct net_device *dev;
|
|
- struct station_stats stats;
|
|
|
|
|
|
+ struct station_info sinfo;
|
|
struct sk_buff *msg;
|
|
struct sk_buff *msg;
|
|
u8 *mac_addr = NULL;
|
|
u8 *mac_addr = NULL;
|
|
|
|
|
|
- memset(&stats, 0, sizeof(stats));
|
|
|
|
|
|
+ memset(&sinfo, 0, sizeof(sinfo));
|
|
|
|
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
if (!info->attrs[NL80211_ATTR_MAC])
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
@@ -926,15 +1021,18 @@ static int nl80211_get_station(struct sk_buff *skb, struct genl_info *info)
|
|
}
|
|
}
|
|
|
|
|
|
rtnl_lock();
|
|
rtnl_lock();
|
|
- err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &stats);
|
|
|
|
|
|
+ err = drv->ops->get_station(&drv->wiphy, dev, mac_addr, &sinfo);
|
|
rtnl_unlock();
|
|
rtnl_unlock();
|
|
|
|
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
if (!msg)
|
|
if (!msg)
|
|
goto out;
|
|
goto out;
|
|
|
|
|
|
if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
|
|
if (nl80211_send_station(msg, info->snd_pid, info->snd_seq, 0,
|
|
- dev, mac_addr, &stats) < 0)
|
|
|
|
|
|
+ dev, mac_addr, &sinfo) < 0)
|
|
goto out_free;
|
|
goto out_free;
|
|
|
|
|
|
err = genlmsg_unicast(msg, info->snd_pid);
|
|
err = genlmsg_unicast(msg, info->snd_pid);
|
|
@@ -1005,6 +1103,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
|
|
¶ms.station_flags))
|
|
¶ms.station_flags))
|
|
return -EINVAL;
|
|
return -EINVAL;
|
|
|
|
|
|
|
|
+ if (info->attrs[NL80211_ATTR_STA_PLINK_ACTION])
|
|
|
|
+ params.plink_action =
|
|
|
|
+ nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
|
|
|
|
+
|
|
err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
if (err)
|
|
if (err)
|
|
return err;
|
|
return err;
|
|
@@ -1119,6 +1221,273 @@ static int nl80211_del_station(struct sk_buff *skb, struct genl_info *info)
|
|
return err;
|
|
return err;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+static int nl80211_send_mpath(struct sk_buff *msg, u32 pid, u32 seq,
|
|
|
|
+ int flags, struct net_device *dev,
|
|
|
|
+ u8 *dst, u8 *next_hop,
|
|
|
|
+ struct mpath_info *pinfo)
|
|
|
|
+{
|
|
|
|
+ void *hdr;
|
|
|
|
+ struct nlattr *pinfoattr;
|
|
|
|
+
|
|
|
|
+ hdr = nl80211hdr_put(msg, pid, seq, flags, NL80211_CMD_NEW_STATION);
|
|
|
|
+ if (!hdr)
|
|
|
|
+ return -1;
|
|
|
|
+
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_ATTR_IFINDEX, dev->ifindex);
|
|
|
|
+ NLA_PUT(msg, NL80211_ATTR_MAC, ETH_ALEN, dst);
|
|
|
|
+ NLA_PUT(msg, NL80211_ATTR_MPATH_NEXT_HOP, ETH_ALEN, next_hop);
|
|
|
|
+
|
|
|
|
+ pinfoattr = nla_nest_start(msg, NL80211_ATTR_MPATH_INFO);
|
|
|
|
+ if (!pinfoattr)
|
|
|
|
+ goto nla_put_failure;
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_FRAME_QLEN)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_MPATH_INFO_FRAME_QLEN,
|
|
|
|
+ pinfo->frame_qlen);
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_DSN)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_MPATH_INFO_DSN,
|
|
|
|
+ pinfo->dsn);
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_METRIC)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_MPATH_INFO_METRIC,
|
|
|
|
+ pinfo->metric);
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_EXPTIME)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_MPATH_INFO_EXPTIME,
|
|
|
|
+ pinfo->exptime);
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_FLAGS)
|
|
|
|
+ NLA_PUT_U8(msg, NL80211_MPATH_INFO_FLAGS,
|
|
|
|
+ pinfo->flags);
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_DISCOVERY_TIMEOUT)
|
|
|
|
+ NLA_PUT_U32(msg, NL80211_MPATH_INFO_DISCOVERY_TIMEOUT,
|
|
|
|
+ pinfo->discovery_timeout);
|
|
|
|
+ if (pinfo->filled & MPATH_INFO_DISCOVERY_RETRIES)
|
|
|
|
+ NLA_PUT_U8(msg, NL80211_MPATH_INFO_DISCOVERY_RETRIES,
|
|
|
|
+ pinfo->discovery_retries);
|
|
|
|
+
|
|
|
|
+ nla_nest_end(msg, pinfoattr);
|
|
|
|
+
|
|
|
|
+ return genlmsg_end(msg, hdr);
|
|
|
|
+
|
|
|
|
+ nla_put_failure:
|
|
|
|
+ return genlmsg_cancel(msg, hdr);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_dump_mpath(struct sk_buff *skb,
|
|
|
|
+ struct netlink_callback *cb)
|
|
|
|
+{
|
|
|
|
+ int wp_idx = 0;
|
|
|
|
+ int if_idx = 0;
|
|
|
|
+ int sta_idx = cb->args[2];
|
|
|
|
+ int wp_start = cb->args[0];
|
|
|
|
+ int if_start = cb->args[1];
|
|
|
|
+ struct mpath_info pinfo;
|
|
|
|
+ struct cfg80211_registered_device *dev;
|
|
|
|
+ struct wireless_dev *wdev;
|
|
|
|
+ u8 dst[ETH_ALEN];
|
|
|
|
+ u8 next_hop[ETH_ALEN];
|
|
|
|
+ int err;
|
|
|
|
+ int exit = 0;
|
|
|
|
+
|
|
|
|
+ /* TODO: filter by device */
|
|
|
|
+ mutex_lock(&cfg80211_drv_mutex);
|
|
|
|
+ list_for_each_entry(dev, &cfg80211_drv_list, list) {
|
|
|
|
+ if (exit)
|
|
|
|
+ break;
|
|
|
|
+ if (++wp_idx < wp_start)
|
|
|
|
+ continue;
|
|
|
|
+ if_idx = 0;
|
|
|
|
+
|
|
|
|
+ mutex_lock(&dev->devlist_mtx);
|
|
|
|
+ list_for_each_entry(wdev, &dev->netdev_list, list) {
|
|
|
|
+ if (exit)
|
|
|
|
+ break;
|
|
|
|
+ if (++if_idx < if_start)
|
|
|
|
+ continue;
|
|
|
|
+ if (!dev->ops->dump_mpath)
|
|
|
|
+ continue;
|
|
|
|
+
|
|
|
|
+ for (;; ++sta_idx) {
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ err = dev->ops->dump_mpath(&dev->wiphy,
|
|
|
|
+ wdev->netdev, sta_idx, dst,
|
|
|
|
+ next_hop, &pinfo);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+ if (err) {
|
|
|
|
+ sta_idx = 0;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ if (nl80211_send_mpath(skb,
|
|
|
|
+ NETLINK_CB(cb->skb).pid,
|
|
|
|
+ cb->nlh->nlmsg_seq, NLM_F_MULTI,
|
|
|
|
+ wdev->netdev, dst, next_hop,
|
|
|
|
+ &pinfo) < 0) {
|
|
|
|
+ exit = 1;
|
|
|
|
+ break;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&dev->devlist_mtx);
|
|
|
|
+ }
|
|
|
|
+ mutex_unlock(&cfg80211_drv_mutex);
|
|
|
|
+
|
|
|
|
+ cb->args[0] = wp_idx;
|
|
|
|
+ cb->args[1] = if_idx;
|
|
|
|
+ cb->args[2] = sta_idx;
|
|
|
|
+
|
|
|
|
+ return skb->len;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_get_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *drv;
|
|
|
|
+ int err;
|
|
|
|
+ struct net_device *dev;
|
|
|
|
+ struct mpath_info pinfo;
|
|
|
|
+ struct sk_buff *msg;
|
|
|
|
+ u8 *dst = NULL;
|
|
|
|
+ u8 next_hop[ETH_ALEN];
|
|
|
|
+
|
|
|
|
+ memset(&pinfo, 0, sizeof(pinfo));
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
+
|
|
|
|
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ if (!drv->ops->get_mpath) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ err = drv->ops->get_mpath(&drv->wiphy, dev, dst, next_hop, &pinfo);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+
|
|
|
|
+ if (err)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ msg = nlmsg_new(NLMSG_GOODSIZE, GFP_KERNEL);
|
|
|
|
+ if (!msg)
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ if (nl80211_send_mpath(msg, info->snd_pid, info->snd_seq, 0,
|
|
|
|
+ dev, dst, next_hop, &pinfo) < 0)
|
|
|
|
+ goto out_free;
|
|
|
|
+
|
|
|
|
+ err = genlmsg_unicast(msg, info->snd_pid);
|
|
|
|
+ goto out;
|
|
|
|
+
|
|
|
|
+ out_free:
|
|
|
|
+ nlmsg_free(msg);
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ cfg80211_put_dev(drv);
|
|
|
|
+ dev_put(dev);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_set_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *drv;
|
|
|
|
+ int err;
|
|
|
|
+ struct net_device *dev;
|
|
|
|
+ u8 *dst = NULL;
|
|
|
|
+ u8 *next_hop = NULL;
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
+ next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
|
|
|
|
+
|
|
|
|
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ if (!drv->ops->change_mpath) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ err = drv->ops->change_mpath(&drv->wiphy, dev, dst, next_hop);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ cfg80211_put_dev(drv);
|
|
|
|
+ dev_put(dev);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+static int nl80211_new_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *drv;
|
|
|
|
+ int err;
|
|
|
|
+ struct net_device *dev;
|
|
|
|
+ u8 *dst = NULL;
|
|
|
|
+ u8 *next_hop = NULL;
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MAC])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ if (!info->attrs[NL80211_ATTR_MPATH_NEXT_HOP])
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
+ next_hop = nla_data(info->attrs[NL80211_ATTR_MPATH_NEXT_HOP]);
|
|
|
|
+
|
|
|
|
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ if (!drv->ops->add_mpath) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ err = drv->ops->add_mpath(&drv->wiphy, dev, dst, next_hop);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ cfg80211_put_dev(drv);
|
|
|
|
+ dev_put(dev);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int nl80211_del_mpath(struct sk_buff *skb, struct genl_info *info)
|
|
|
|
+{
|
|
|
|
+ struct cfg80211_registered_device *drv;
|
|
|
|
+ int err;
|
|
|
|
+ struct net_device *dev;
|
|
|
|
+ u8 *dst = NULL;
|
|
|
|
+
|
|
|
|
+ if (info->attrs[NL80211_ATTR_MAC])
|
|
|
|
+ dst = nla_data(info->attrs[NL80211_ATTR_MAC]);
|
|
|
|
+
|
|
|
|
+ err = get_drv_dev_by_info_ifindex(info, &drv, &dev);
|
|
|
|
+ if (err)
|
|
|
|
+ return err;
|
|
|
|
+
|
|
|
|
+ if (!drv->ops->del_mpath) {
|
|
|
|
+ err = -EOPNOTSUPP;
|
|
|
|
+ goto out;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ rtnl_lock();
|
|
|
|
+ err = drv->ops->del_mpath(&drv->wiphy, dev, dst);
|
|
|
|
+ rtnl_unlock();
|
|
|
|
+
|
|
|
|
+ out:
|
|
|
|
+ cfg80211_put_dev(drv);
|
|
|
|
+ dev_put(dev);
|
|
|
|
+ return err;
|
|
|
|
+}
|
|
|
|
+
|
|
static struct genl_ops nl80211_ops[] = {
|
|
static struct genl_ops nl80211_ops[] = {
|
|
{
|
|
{
|
|
.cmd = NL80211_CMD_GET_WIPHY,
|
|
.cmd = NL80211_CMD_GET_WIPHY,
|
|
@@ -1203,7 +1572,7 @@ static struct genl_ops nl80211_ops[] = {
|
|
{
|
|
{
|
|
.cmd = NL80211_CMD_GET_STATION,
|
|
.cmd = NL80211_CMD_GET_STATION,
|
|
.doit = nl80211_get_station,
|
|
.doit = nl80211_get_station,
|
|
- /* TODO: implement dumpit */
|
|
|
|
|
|
+ .dumpit = nl80211_dump_station,
|
|
.policy = nl80211_policy,
|
|
.policy = nl80211_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
},
|
|
@@ -1225,6 +1594,31 @@ static struct genl_ops nl80211_ops[] = {
|
|
.policy = nl80211_policy,
|
|
.policy = nl80211_policy,
|
|
.flags = GENL_ADMIN_PERM,
|
|
.flags = GENL_ADMIN_PERM,
|
|
},
|
|
},
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_GET_MPATH,
|
|
|
|
+ .doit = nl80211_get_mpath,
|
|
|
|
+ .dumpit = nl80211_dump_mpath,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_SET_MPATH,
|
|
|
|
+ .doit = nl80211_set_mpath,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_NEW_MPATH,
|
|
|
|
+ .doit = nl80211_new_mpath,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ },
|
|
|
|
+ {
|
|
|
|
+ .cmd = NL80211_CMD_DEL_MPATH,
|
|
|
|
+ .doit = nl80211_del_mpath,
|
|
|
|
+ .policy = nl80211_policy,
|
|
|
|
+ .flags = GENL_ADMIN_PERM,
|
|
|
|
+ },
|
|
};
|
|
};
|
|
|
|
|
|
/* multicast groups */
|
|
/* multicast groups */
|