|
@@ -15,6 +15,11 @@
|
|
|
#include "ieee80211_i.h"
|
|
|
#include "cfg.h"
|
|
|
#include "ieee80211_rate.h"
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+#include "mesh.h"
|
|
|
+#endif
|
|
|
+
|
|
|
+#define DEFAULT_RATES 0
|
|
|
|
|
|
static enum ieee80211_if_types
|
|
|
nl80211_type_to_mac80211_type(enum nl80211_iftype type)
|
|
@@ -28,6 +33,10 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
|
|
|
return IEEE80211_IF_TYPE_STA;
|
|
|
case NL80211_IFTYPE_MONITOR:
|
|
|
return IEEE80211_IF_TYPE_MNTR;
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ case NL80211_IFTYPE_MESH_POINT:
|
|
|
+ return IEEE80211_IF_TYPE_MESH_POINT;
|
|
|
+#endif
|
|
|
default:
|
|
|
return IEEE80211_IF_TYPE_INVALID;
|
|
|
}
|
|
@@ -110,6 +119,15 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
|
|
|
ieee80211_if_reinit(dev);
|
|
|
ieee80211_if_set_type(dev, itype);
|
|
|
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT &&
|
|
|
+ params->mesh_id_len) {
|
|
|
+ sdata->u.sta.mesh_id_len = params->mesh_id_len;
|
|
|
+ memcpy(sdata->u.sta.mesh_id, params->mesh_id,
|
|
|
+ params->mesh_id_len);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+
|
|
|
if (sdata->vif.type != IEEE80211_IF_TYPE_MNTR || !flags)
|
|
|
return 0;
|
|
|
|
|
@@ -297,6 +315,51 @@ static int ieee80211_config_default_key(struct wiphy *wiphy,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+static void sta_set_sinfo(struct sta_info *sta, struct station_info *sinfo)
|
|
|
+{
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
|
|
|
+#endif
|
|
|
+
|
|
|
+ sinfo->filled = STATION_INFO_INACTIVE_TIME |
|
|
|
+ STATION_INFO_RX_BYTES |
|
|
|
+ STATION_INFO_TX_BYTES;
|
|
|
+
|
|
|
+ sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
|
|
|
+ sinfo->rx_bytes = sta->rx_bytes;
|
|
|
+ sinfo->tx_bytes = sta->tx_bytes;
|
|
|
+
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT) {
|
|
|
+ sinfo->filled |= STATION_INFO_LLID |
|
|
|
+ STATION_INFO_PLID |
|
|
|
+ STATION_INFO_PLINK_STATE;
|
|
|
+
|
|
|
+ sinfo->llid = le16_to_cpu(sta->llid);
|
|
|
+ sinfo->plid = le16_to_cpu(sta->plid);
|
|
|
+ sinfo->plink_state = sta->plink_state;
|
|
|
+ }
|
|
|
+#endif
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
+static int ieee80211_dump_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
+ int idx, u8 *mac, struct station_info *sinfo)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
+ struct sta_info *sta;
|
|
|
+
|
|
|
+ sta = sta_info_get_by_idx(local, idx, dev);
|
|
|
+ if (!sta)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ memcpy(mac, sta->addr, ETH_ALEN);
|
|
|
+ sta_set_sinfo(sta, sinfo);
|
|
|
+ sta_info_put(sta);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
u8 *mac, struct station_info *sinfo)
|
|
|
{
|
|
@@ -308,15 +371,7 @@ static int ieee80211_get_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
return -ENOENT;
|
|
|
|
|
|
/* XXX: verify sta->dev == dev */
|
|
|
-
|
|
|
- sinfo->filled = STATION_INFO_INACTIVE_TIME |
|
|
|
- STATION_INFO_RX_BYTES |
|
|
|
- STATION_INFO_TX_BYTES;
|
|
|
-
|
|
|
- sinfo->inactive_time = jiffies_to_msecs(jiffies - sta->last_rx);
|
|
|
- sinfo->rx_bytes = sta->rx_bytes;
|
|
|
- sinfo->tx_bytes = sta->tx_bytes;
|
|
|
-
|
|
|
+ sta_set_sinfo(sta, sinfo);
|
|
|
sta_info_put(sta);
|
|
|
|
|
|
return 0;
|
|
@@ -525,6 +580,9 @@ static void sta_apply_parameters(struct ieee80211_local *local,
|
|
|
u32 rates;
|
|
|
int i, j;
|
|
|
struct ieee80211_supported_band *sband;
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(sta->dev);
|
|
|
+#endif
|
|
|
|
|
|
if (params->station_flags & STATION_FLAG_CHANGED) {
|
|
|
sta->flags &= ~WLAN_STA_AUTHORIZED;
|
|
@@ -562,6 +620,19 @@ static void sta_apply_parameters(struct ieee80211_local *local,
|
|
|
}
|
|
|
sta->supp_rates[local->oper_channel->band] = rates;
|
|
|
}
|
|
|
+
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT &&
|
|
|
+ params->plink_action)
|
|
|
+ switch (params->plink_action) {
|
|
|
+ case PLINK_ACTION_OPEN:
|
|
|
+ mesh_plink_open(sta);
|
|
|
+ break;
|
|
|
+ case PLINK_ACTION_BLOCK:
|
|
|
+ mesh_plink_block(sta);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+#endif
|
|
|
}
|
|
|
|
|
|
static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
|
@@ -584,7 +655,13 @@ static int ieee80211_add_station(struct wiphy *wiphy, struct net_device *dev,
|
|
|
} else
|
|
|
sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
|
|
|
- sta = sta_info_add(local, dev, mac, GFP_KERNEL);
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ if (sdata->vif.type == IEEE80211_IF_TYPE_MESH_POINT)
|
|
|
+ sta = mesh_plink_add(mac, DEFAULT_RATES, dev);
|
|
|
+ else
|
|
|
+#endif
|
|
|
+ sta = sta_info_add(local, dev, mac, GFP_KERNEL);
|
|
|
+
|
|
|
if (IS_ERR(sta))
|
|
|
return PTR_ERR(sta);
|
|
|
|
|
@@ -656,6 +733,170 @@ static int ieee80211_change_station(struct wiphy *wiphy,
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+static int ieee80211_add_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
+ u8 *dst, u8 *next_hop)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
+ struct mesh_path *mpath;
|
|
|
+ struct sta_info *sta;
|
|
|
+ int err;
|
|
|
+
|
|
|
+ if (!netif_running(dev))
|
|
|
+ return -ENETDOWN;
|
|
|
+
|
|
|
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ sta = sta_info_get(local, next_hop);
|
|
|
+ if (!sta)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ err = mesh_path_add(dst, dev);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ mpath = mesh_path_lookup(dst, dev);
|
|
|
+ if (!mpath) {
|
|
|
+ rcu_read_unlock();
|
|
|
+ sta_info_put(sta);
|
|
|
+ return -ENXIO;
|
|
|
+ }
|
|
|
+ mesh_path_fix_nexthop(mpath, sta);
|
|
|
+ sta_info_put(sta);
|
|
|
+ rcu_read_unlock();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ieee80211_del_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
+ u8 *dst)
|
|
|
+{
|
|
|
+ if (dst)
|
|
|
+ return mesh_path_del(dst, dev);
|
|
|
+
|
|
|
+ mesh_path_flush(dev);
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ieee80211_change_mpath(struct wiphy *wiphy,
|
|
|
+ struct net_device *dev,
|
|
|
+ u8 *dst, u8 *next_hop)
|
|
|
+{
|
|
|
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
+ struct mesh_path *mpath;
|
|
|
+ struct sta_info *sta;
|
|
|
+
|
|
|
+ if (!netif_running(dev))
|
|
|
+ return -ENETDOWN;
|
|
|
+
|
|
|
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ sta = sta_info_get(local, next_hop);
|
|
|
+ if (!sta)
|
|
|
+ return -ENOENT;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ mpath = mesh_path_lookup(dst, dev);
|
|
|
+ if (!mpath) {
|
|
|
+ rcu_read_unlock();
|
|
|
+ sta_info_put(sta);
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+
|
|
|
+ mesh_path_fix_nexthop(mpath, sta);
|
|
|
+ sta_info_put(sta);
|
|
|
+ rcu_read_unlock();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static void mpath_set_pinfo(struct mesh_path *mpath, u8 *next_hop,
|
|
|
+ struct mpath_info *pinfo)
|
|
|
+{
|
|
|
+ if (mpath->next_hop)
|
|
|
+ memcpy(next_hop, mpath->next_hop->addr, ETH_ALEN);
|
|
|
+ else
|
|
|
+ memset(next_hop, 0, ETH_ALEN);
|
|
|
+
|
|
|
+ pinfo->filled = MPATH_INFO_FRAME_QLEN |
|
|
|
+ MPATH_INFO_DSN |
|
|
|
+ MPATH_INFO_METRIC |
|
|
|
+ MPATH_INFO_EXPTIME |
|
|
|
+ MPATH_INFO_DISCOVERY_TIMEOUT |
|
|
|
+ MPATH_INFO_DISCOVERY_RETRIES |
|
|
|
+ MPATH_INFO_FLAGS;
|
|
|
+
|
|
|
+ pinfo->frame_qlen = mpath->frame_queue.qlen;
|
|
|
+ pinfo->dsn = mpath->dsn;
|
|
|
+ pinfo->metric = mpath->metric;
|
|
|
+ if (time_before(jiffies, mpath->exp_time))
|
|
|
+ pinfo->exptime = jiffies_to_msecs(mpath->exp_time - jiffies);
|
|
|
+ pinfo->discovery_timeout =
|
|
|
+ jiffies_to_msecs(mpath->discovery_timeout);
|
|
|
+ pinfo->discovery_retries = mpath->discovery_retries;
|
|
|
+ pinfo->flags = 0;
|
|
|
+ if (mpath->flags & MESH_PATH_ACTIVE)
|
|
|
+ pinfo->flags |= NL80211_MPATH_FLAG_ACTIVE;
|
|
|
+ if (mpath->flags & MESH_PATH_RESOLVING)
|
|
|
+ pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
|
|
|
+ if (mpath->flags & MESH_PATH_DSN_VALID)
|
|
|
+ pinfo->flags |= NL80211_MPATH_FLAG_DSN_VALID;
|
|
|
+ if (mpath->flags & MESH_PATH_FIXED)
|
|
|
+ pinfo->flags |= NL80211_MPATH_FLAG_FIXED;
|
|
|
+ if (mpath->flags & MESH_PATH_RESOLVING)
|
|
|
+ pinfo->flags |= NL80211_MPATH_FLAG_RESOLVING;
|
|
|
+
|
|
|
+ pinfo->flags = mpath->flags;
|
|
|
+}
|
|
|
+
|
|
|
+static int ieee80211_get_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
+ u8 *dst, u8 *next_hop, struct mpath_info *pinfo)
|
|
|
+
|
|
|
+{
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
+ struct mesh_path *mpath;
|
|
|
+
|
|
|
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ mpath = mesh_path_lookup(dst, dev);
|
|
|
+ if (!mpath) {
|
|
|
+ rcu_read_unlock();
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+ memcpy(dst, mpath->dst, ETH_ALEN);
|
|
|
+ mpath_set_pinfo(mpath, next_hop, pinfo);
|
|
|
+ rcu_read_unlock();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int ieee80211_dump_mpath(struct wiphy *wiphy, struct net_device *dev,
|
|
|
+ int idx, u8 *dst, u8 *next_hop,
|
|
|
+ struct mpath_info *pinfo)
|
|
|
+{
|
|
|
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
|
|
|
+ struct mesh_path *mpath;
|
|
|
+
|
|
|
+ if (sdata->vif.type != IEEE80211_IF_TYPE_MESH_POINT)
|
|
|
+ return -ENOTSUPP;
|
|
|
+
|
|
|
+ rcu_read_lock();
|
|
|
+ mpath = mesh_path_lookup_by_idx(idx, dev);
|
|
|
+ if (!mpath) {
|
|
|
+ rcu_read_unlock();
|
|
|
+ return -ENOENT;
|
|
|
+ }
|
|
|
+ memcpy(dst, mpath->dst, ETH_ALEN);
|
|
|
+ mpath_set_pinfo(mpath, next_hop, pinfo);
|
|
|
+ rcu_read_unlock();
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
struct cfg80211_ops mac80211_config_ops = {
|
|
|
.add_virtual_intf = ieee80211_add_iface,
|
|
|
.del_virtual_intf = ieee80211_del_iface,
|
|
@@ -671,4 +912,12 @@ struct cfg80211_ops mac80211_config_ops = {
|
|
|
.del_station = ieee80211_del_station,
|
|
|
.change_station = ieee80211_change_station,
|
|
|
.get_station = ieee80211_get_station,
|
|
|
+ .dump_station = ieee80211_dump_station,
|
|
|
+#ifdef CONFIG_MAC80211_MESH
|
|
|
+ .add_mpath = ieee80211_add_mpath,
|
|
|
+ .del_mpath = ieee80211_del_mpath,
|
|
|
+ .change_mpath = ieee80211_change_mpath,
|
|
|
+ .get_mpath = ieee80211_get_mpath,
|
|
|
+ .dump_mpath = ieee80211_dump_mpath,
|
|
|
+#endif
|
|
|
};
|