@@ -713,6 +713,7 @@ enum nl80211_attrs {
NL80211_ATTR_KEYS,
NL80211_ATTR_PID,
+ NL80211_ATTR_SIM_LOSS,
/* add attributes here, update the policy in nl80211.c */
@@ -1198,6 +1199,8 @@ enum nl80211_mntr_flags {
*
* @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
*
+ * @NL80211_MESHCONF_SIM_LOSS_RATE: simulated loss rate at this MP.
+ *
* @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
*/
enum nl80211_meshconf_params {
@@ -1215,6 +1218,7 @@ enum nl80211_meshconf_params {
NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT,
NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL,
NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
+ NL80211_MESHCONF_SIM_LOSS_RATE,
/* keep last */
__NL80211_MESHCONF_ATTR_AFTER_LAST,
@@ -265,6 +265,7 @@ enum plink_actions {
PLINK_ACTION_INVALID,
PLINK_ACTION_OPEN,
PLINK_ACTION_BLOCK,
+ PLINK_ACTION_LOSS,
};
/**
@@ -282,6 +283,7 @@ enum plink_actions {
* (bitmask of BIT(NL80211_STA_FLAG_...))
* @listen_interval: listen interval or -1 for no change
* @aid: AID or zero for no change
+ * @sim_loss_rate: simulated TX probable loss percentage
*/
struct station_parameters {
u8 *supported_rates;
@@ -292,6 +294,7 @@ struct station_parameters {
u8 supported_rates_len;
u8 plink_action;
struct ieee80211_ht_cap *ht_capa;
+ u8 sim_loss_rate;
};
/**
@@ -508,6 +511,7 @@ struct mesh_config {
u32 dot11MeshHWMPactivePathTimeout;
u16 dot11MeshHWMPpreqMinInterval;
u16 dot11MeshHWMPnetDiameterTraversalTime;
+ u8 dot11MeshSimLossRate;
};
/**
@@ -693,6 +693,9 @@ static void sta_apply_parameters(struct ieee80211_local *local,
case PLINK_ACTION_BLOCK:
mesh_plink_block(sta);
break;
+ case PLINK_ACTION_LOSS:
+ mesh_plink_sim_loss(sta, params->sim_loss_rate);
+ break;
}
}
}
@@ -1043,6 +1046,9 @@ static int ieee80211_set_mesh_params(struct wiphy *wiphy,
mask))
conf->dot11MeshHWMPnetDiameterTraversalTime =
nconf->dot11MeshHWMPnetDiameterTraversalTime;
+ if (_chg_mesh_attr(NL80211_MESHCONF_SIM_LOSS_RATE, mask))
+ conf->dot11MeshSimLossRate =
+ nconf->dot11MeshSimLossRate;
return 0;
}
@@ -653,6 +653,7 @@ void ieee80211_mesh_init_sdata(struct ieee80211_sub_if_data *sdata)
MESH_PATH_REFRESH_TIME;
ifmsh->mshcfg.min_discovery_timeout =
MESH_MIN_DISCOVERY_TIMEOUT;
+ ifmsh->mshcfg.dot11MeshSimLossRate = 0;
ifmsh->accepting_plinks = true;
ifmsh->preq_id = 0;
ifmsh->dsn = 0;
@@ -238,6 +238,7 @@ void mesh_plink_broken(struct sta_info *sta);
void mesh_plink_deactivate(struct sta_info *sta);
int mesh_plink_open(struct sta_info *sta);
void mesh_plink_block(struct sta_info *sta);
+void mesh_plink_sim_loss(struct sta_info *sta, u8 rate);
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata,
struct ieee80211_mgmt *mgmt, size_t len,
struct ieee80211_rx_status *rx_status);
@@ -272,6 +272,7 @@ static u32 hwmp_route_info_get(struct ieee80211_sub_if_data *sdata,
}
last_hop_metric = airtime_link_metric_get(local, sta);
+ printk("XXX: last_hop_metric = %d\n", last_hop_metric);
/* Update and check originator routing info */
fresh_info = true;
@@ -382,6 +382,12 @@ void mesh_plink_block(struct sta_info *sta)
spin_unlock_bh(&sta->lock);
}
+void mesh_plink_sim_loss(struct sta_info *sta, u8 rate)
+{
+ spin_lock_bh(&sta->lock);
+ sta->plink_sim_loss = rate;
+ spin_unlock_bh(&sta->lock);
+}
void mesh_rx_plink_frame(struct ieee80211_sub_if_data *sdata, struct ieee80211_mgmt *mgmt,
size_t len, struct ieee80211_rx_status *rx_status)
@@ -294,6 +294,7 @@ struct sta_info {
bool ignore_plink_timer;
bool plink_timer_was_running;
enum plink_state plink_state;
+ u8 plink_sim_loss;
u32 plink_timeout;
struct timer_list plink_timer;
#endif
@@ -18,6 +18,7 @@
#include <linux/etherdevice.h>
#include <linux/bitmap.h>
#include <linux/rcupdate.h>
+#include <linux/random.h>
#include <net/net_namespace.h>
#include <net/ieee80211_radiotap.h>
#include <net/cfg80211.h>
@@ -39,6 +40,47 @@
/* misc utils */
+/* Decide whether we should simulate the loss of a frame based on the simulated
+ * plink loss probability and the frame's destination. Used for testing.
+ *
+ * Must be invoked with the rcu read lock held.
+ * */
+static int random_tx_loss(struct ieee80211_sub_if_data *sdata, struct
+ ieee80211_hdr *hdr)
+{
+ struct sta_info *sta;
+ u8 r = 0;
+
+ get_random_bytes(&r, sizeof(r));
+
+ list_for_each_entry(sta, &sdata->local->sta_list, list) {
+ if (!memcmp(sta->sta.addr, hdr->addr1, 6))
+ if (r*25 < sta->plink_sim_loss*64)
+ return 1;
+ }
+
+ return 0;
+}
+
+
+static int simulate_tx_loss(struct ieee80211_sub_if_data *sdata,
+ struct ieee80211_hdr *hdr, struct sk_buff *skb)
+{
+ struct ieee80211_hw *hw = &sdata->local->hw;
+ struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+
+ ieee80211_tx_info_clear_status(info);
+ info->flags = IEEE80211_TX_CTL_REQ_TX_STATUS;
+
+ info->status.rates[0].idx = 0;
+ info->status.rates[0].count = hw->max_rate_tries;
+
+ ieee80211_tx_status_irqsafe(hw, skb);
+
+ /* this will ensure that the frame is not queued in the tx path */
+ return IEEE80211_TX_OK;
+}
+
static __le16 ieee80211_duration(struct ieee80211_tx_data *tx, int group_addr,
int next_frag_len)
{
@@ -1244,6 +1286,7 @@ static void ieee80211_tx(struct ieee80211_sub_if_data *sdata,
struct ieee80211_tx_data tx;
ieee80211_tx_result res_prepare;
struct ieee80211_tx_info *info = IEEE80211_SKB_CB(skb);
+ struct ieee80211_hdr *hdr = (struct ieee80211_hdr *) skb->data;
struct sk_buff *next;
unsigned long flags;
int ret, retries;
@@ -1278,7 +1321,10 @@ static void ieee80211_tx(struct ieee80211_sub_if_data *sdata,
retries = 0;
retry:
- ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending);
+ if (ieee80211_vif_is_mesh(&sdata->vif) && random_tx_loss(sdata, hdr))
+ ret = simulate_tx_loss(sdata, hdr, skb);
+ else
+ ret = __ieee80211_tx(local, &tx.skb, tx.sta, txpending);
switch (ret) {
case IEEE80211_TX_OK:
break;
@@ -1857,7 +1903,11 @@ static bool ieee80211_tx_pending_skb(struct ieee80211_local *local,
hdr = (struct ieee80211_hdr *)skb->data;
sta = sta_info_get(local, hdr->addr1);
- ret = __ieee80211_tx(local, &skb, sta, true);
+ if (ieee80211_vif_is_mesh(&sdata->vif) &&
+ random_tx_loss(sdata, hdr))
+ ret = simulate_tx_loss(sdata, hdr, skb);
+ else
+ ret = __ieee80211_tx(local, &skb, sta, true);
if (ret != IEEE80211_TX_OK)
result = false;
}
@@ -1878,6 +1878,10 @@ static int nl80211_set_station(struct sk_buff *skb, struct genl_info *info)
params.plink_action =
nla_get_u8(info->attrs[NL80211_ATTR_STA_PLINK_ACTION]);
+ if (info->attrs[NL80211_ATTR_SIM_LOSS])
+ params.sim_loss_rate =
+ nla_get_u8(info->attrs[NL80211_ATTR_SIM_LOSS]);
+
rtnl_lock();
err = get_rdev_dev_by_info_ifindex(info, &rdev, &dev);
@@ -2619,6 +2623,8 @@ static int nl80211_get_mesh_params(struct sk_buff *skb,
cur_params.dot11MeshHWMPpreqMinInterval);
NLA_PUT_U16(msg, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
cur_params.dot11MeshHWMPnetDiameterTraversalTime);
+ NLA_PUT_U8(msg, NL80211_MESHCONF_SIM_LOSS_RATE,
+ cur_params.dot11MeshSimLossRate);
nla_nest_end(msg, pinfoattr);
genlmsg_end(msg, hdr);
err = genlmsg_reply(msg, info);
@@ -2661,6 +2667,7 @@ nl80211_meshconf_params_policy[NL80211_MESHCONF_ATTR_MAX+1] __read_mostly = {
[NL80211_MESHCONF_HWMP_ACTIVE_PATH_TIMEOUT] = { .type = NLA_U32 },
[NL80211_MESHCONF_HWMP_PREQ_MIN_INTERVAL] = { .type = NLA_U16 },
[NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
+ [NL80211_MESHCONF_SIM_LOSS_RATE] = { .type = NLA_U8 },
};
static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
@@ -2729,6 +2736,8 @@ static int nl80211_set_mesh_params(struct sk_buff *skb, struct genl_info *info)
dot11MeshHWMPnetDiameterTraversalTime,
mask, NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME,
nla_get_u16);
+ FILL_IN_MESH_PARAM_IF_SET(tb, cfg, dot11MeshSimLossRate,
+ mask, NL80211_MESHCONF_SIM_LOSS_RATE, nla_get_u8);
/* Apply changes */
err = rdev->ops->set_mesh_params(&rdev->wiphy, dev, &cfg, mask);