summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorJavier Cardona <javier@cozybit.com>2010-12-16 17:37:49 -0800
committerJohn W. Linville <linville@tuxdriver.com>2010-12-20 14:46:57 -0500
commitc80d545da3f7c0e534ccd4a780f322f80a92cff1 (patch)
treeedd5c51676b4677fc1a0b2fc692ffe97df863f25 /net
parent24bdd9f4c9af75b33b438d60381a67626de0128d (diff)
mac80211: Let userspace enable and configure vendor specific path selection.
Userspace will now be allowed to toggle between the default path selection algorithm (HWMP, implemented in the kernel), and a vendor specific alternative. Also in the same patch, allow userspace to add information elements to mesh beacons. This is accordance with the Extensible Path Selection Framework specified in version 7.0 of the 802.11s draft. Signed-off-by: Javier Cardona <javier@cozybit.com> Signed-off-by: John W. Linville <linville@tuxdriver.com>
Diffstat (limited to 'net')
-rw-r--r--net/mac80211/cfg.c39
-rw-r--r--net/mac80211/ieee80211_i.h4
-rw-r--r--net/mac80211/mesh.c7
-rw-r--r--net/mac80211/mesh_plink.c3
-rw-r--r--net/mac80211/tx.c3
-rw-r--r--net/wireless/core.c22
-rw-r--r--net/wireless/core.h5
-rw-r--r--net/wireless/mesh.c24
-rw-r--r--net/wireless/nl80211.c63
9 files changed, 138 insertions, 32 deletions
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 1c94a2ae22e..ae2c7127a8a 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -1000,6 +1000,36 @@ static inline bool _chg_mesh_attr(enum nl80211_meshconf_params parm, u32 mask)
return (mask >> (parm-1)) & 0x1;
}
+static int copy_mesh_setup(struct ieee80211_if_mesh *ifmsh,
+ const struct mesh_setup *setup)
+{
+ u8 *new_ie;
+ const u8 *old_ie;
+
+ /* first allocate the new vendor information element */
+ new_ie = NULL;
+ old_ie = ifmsh->vendor_ie;
+
+ ifmsh->vendor_ie_len = setup->vendor_ie_len;
+ if (setup->vendor_ie_len) {
+ new_ie = kmemdup(setup->vendor_ie, setup->vendor_ie_len,
+ GFP_KERNEL);
+ if (!new_ie)
+ return -ENOMEM;
+ }
+
+ /* now copy the rest of the setup parameters */
+ ifmsh->mesh_id_len = setup->mesh_id_len;
+ memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
+ ifmsh->mesh_pp_id = setup->path_sel_proto;
+ ifmsh->mesh_pm_id = setup->path_metric;
+ ifmsh->vendor_ie = new_ie;
+
+ kfree(old_ie);
+
+ return 0;
+}
+
static int ieee80211_update_mesh_config(struct wiphy *wiphy,
struct net_device *dev, u32 mask,
const struct mesh_config *nconf)
@@ -1059,11 +1089,12 @@ static int ieee80211_join_mesh(struct wiphy *wiphy, struct net_device *dev,
{
struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
struct ieee80211_if_mesh *ifmsh = &sdata->u.mesh;
+ int err;
- memcpy(&sdata->u.mesh.mshcfg, conf, sizeof(struct mesh_config));
- ifmsh->mesh_id_len = setup->mesh_id_len;
- memcpy(ifmsh->mesh_id, setup->mesh_id, ifmsh->mesh_id_len);
-
+ memcpy(&ifmsh->mshcfg, conf, sizeof(struct mesh_config));
+ err = copy_mesh_setup(ifmsh, setup);
+ if (err)
+ return err;
ieee80211_start_mesh(sdata);
return 0;
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index ce58b2a676e..eadaa243a3d 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -484,6 +484,8 @@ struct ieee80211_if_mesh {
struct mesh_config mshcfg;
u32 mesh_seqnum;
bool accepting_plinks;
+ const u8 *vendor_ie;
+ u8 vendor_ie_len;
};
#ifdef CONFIG_MAC80211_MESH
@@ -585,9 +587,7 @@ struct ieee80211_sub_if_data {
struct ieee80211_if_vlan vlan;
struct ieee80211_if_managed mgd;
struct ieee80211_if_ibss ibss;
-#ifdef CONFIG_MAC80211_MESH
struct ieee80211_if_mesh mesh;
-#endif
u32 mntr_flags;
} u;
diff --git a/net/mac80211/mesh.c b/net/mac80211/mesh.c
index 63e1188d506..c326e009389 100644
--- a/net/mac80211/mesh.c
+++ b/net/mac80211/mesh.c
@@ -287,6 +287,13 @@ void mesh_mgmt_ies_add(struct sk_buff *skb, struct ieee80211_sub_if_data *sdata)
*pos++ |= sdata->u.mesh.accepting_plinks ?
MESHCONF_CAPAB_ACCEPT_PLINKS : 0x00;
*pos++ = 0x00;
+
+ if (sdata->u.mesh.vendor_ie) {
+ int len = sdata->u.mesh.vendor_ie_len;
+ const u8 *data = sdata->u.mesh.vendor_ie;
+ if (skb_tailroom(skb) > len)
+ memcpy(skb_put(skb, len), data, len);
+ }
}
u32 mesh_table_hash(u8 *addr, struct ieee80211_sub_if_data *sdata, struct mesh_table *tbl)
diff --git a/net/mac80211/mesh_plink.c b/net/mac80211/mesh_plink.c
index 1c91f0f3c30..44b53931ba5 100644
--- a/net/mac80211/mesh_plink.c
+++ b/net/mac80211/mesh_plink.c
@@ -160,7 +160,8 @@ static int mesh_plink_frame_tx(struct ieee80211_sub_if_data *sdata,
enum plink_frame_type action, u8 *da, __le16 llid, __le16 plid,
__le16 reason) {
struct ieee80211_local *local = sdata->local;
- struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400 +
+ sdata->u.mesh.vendor_ie_len);
struct ieee80211_mgmt *mgmt;
bool include_plid = false;
static const u8 meshpeeringproto[] = { 0x00, 0x0F, 0xAC, 0x2A };
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 157bde993ef..f4b1b624ea9 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -2290,7 +2290,8 @@ struct sk_buff *ieee80211_beacon_get_tim(struct ieee80211_hw *hw,
u8 *pos;
/* headroom, head length, tail length and maximum TIM length */
- skb = dev_alloc_skb(local->tx_headroom + 400);
+ skb = dev_alloc_skb(local->tx_headroom + 400 +
+ sdata->u.mesh.vendor_ie_len);
if (!skb)
goto out;
diff --git a/net/wireless/core.c b/net/wireless/core.c
index 79772fcc37b..e9a5f8ca4c2 100644
--- a/net/wireless/core.c
+++ b/net/wireless/core.c
@@ -789,13 +789,23 @@ static int cfg80211_netdev_notifier_call(struct notifier_block * nb,
cfg80211_mgd_wext_connect(rdev, wdev);
break;
#endif
+#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
- /* backward compat code ... */
- if (wdev->mesh_id_up_len)
- __cfg80211_join_mesh(rdev, dev, wdev->ssid,
- wdev->mesh_id_up_len,
- &default_mesh_config);
- break;
+ {
+ /* backward compat code... */
+ struct mesh_setup setup;
+ memcpy(&setup, &default_mesh_setup,
+ sizeof(setup));
+ /* back compat only needed for mesh_id */
+ setup.mesh_id = wdev->ssid;
+ setup.mesh_id_len = wdev->mesh_id_up_len;
+ if (wdev->mesh_id_up_len)
+ __cfg80211_join_mesh(rdev, dev,
+ &setup,
+ &default_mesh_config);
+ break;
+ }
+#endif
default:
break;
}
diff --git a/net/wireless/core.h b/net/wireless/core.h
index 743203bb61a..26a0a084e16 100644
--- a/net/wireless/core.h
+++ b/net/wireless/core.h
@@ -287,13 +287,14 @@ int cfg80211_ibss_wext_join(struct cfg80211_registered_device *rdev,
/* mesh */
extern const struct mesh_config default_mesh_config;
+extern const struct mesh_setup default_mesh_setup;
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const u8 *mesh_id, u8 mesh_id_len,
+ const struct mesh_setup *setup,
const struct mesh_config *conf);
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const u8 *mesh_id, u8 mesh_id_len,
+ const struct mesh_setup *setup,
const struct mesh_config *conf);
int cfg80211_leave_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev);
diff --git a/net/wireless/mesh.c b/net/wireless/mesh.c
index e0b9747fe50..73e39c171ff 100644
--- a/net/wireless/mesh.c
+++ b/net/wireless/mesh.c
@@ -50,17 +50,19 @@ const struct mesh_config default_mesh_config = {
.min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT,
};
+const struct mesh_setup default_mesh_setup = {
+ .path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP,
+ .path_metric = IEEE80211_PATH_METRIC_AIRTIME,
+ .vendor_ie = NULL,
+ .vendor_ie_len = 0,
+};
int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const u8 *mesh_id, u8 mesh_id_len,
+ const struct mesh_setup *setup,
const struct mesh_config *conf)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
- struct mesh_setup setup = {
- .mesh_id = mesh_id,
- .mesh_id_len = mesh_id_len,
- };
int err;
BUILD_BUG_ON(IEEE80211_MAX_SSID_LEN != IEEE80211_MAX_MESH_ID_LEN);
@@ -73,16 +75,16 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
if (wdev->mesh_id_len)
return -EALREADY;
- if (!mesh_id_len)
+ if (!setup->mesh_id_len)
return -EINVAL;
if (!rdev->ops->join_mesh)
return -EOPNOTSUPP;
- err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, &setup);
+ err = rdev->ops->join_mesh(&rdev->wiphy, dev, conf, setup);
if (!err) {
- memcpy(wdev->ssid, mesh_id, mesh_id_len);
- wdev->mesh_id_len = mesh_id_len;
+ memcpy(wdev->ssid, setup->mesh_id, setup->mesh_id_len);
+ wdev->mesh_id_len = setup->mesh_id_len;
}
return err;
@@ -90,14 +92,14 @@ int __cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
int cfg80211_join_mesh(struct cfg80211_registered_device *rdev,
struct net_device *dev,
- const u8 *mesh_id, u8 mesh_id_len,
+ const struct mesh_setup *setup,
const struct mesh_config *conf)
{
struct wireless_dev *wdev = dev->ieee80211_ptr;
int err;
wdev_lock(wdev);
- err = __cfg80211_join_mesh(rdev, dev, mesh_id, mesh_id_len, conf);
+ err = __cfg80211_join_mesh(rdev, dev, setup, conf);
wdev_unlock(wdev);
return err;
diff --git a/net/wireless/nl80211.c b/net/wireless/nl80211.c
index 10be9350752..eef89d0b558 100644
--- a/net/wireless/nl80211.c
+++ b/net/wireless/nl80211.c
@@ -2773,6 +2773,14 @@ static const struct nla_policy nl80211_meshconf_params_policy[NL80211_MESHCONF_A
[NL80211_MESHCONF_HWMP_NET_DIAM_TRVS_TIME] = { .type = NLA_U16 },
};
+static const struct nla_policy
+ nl80211_mesh_setup_params_policy[NL80211_MESH_SETUP_ATTR_MAX+1] = {
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL] = { .type = NLA_U8 },
+ [NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC] = { .type = NLA_U8 },
+ [NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE] = { .type = NLA_BINARY,
+ .len = IEEE80211_MAX_DATA_LEN },
+};
+
static int nl80211_parse_mesh_config(struct genl_info *info,
struct mesh_config *cfg,
u32 *mask_out)
@@ -2839,14 +2847,50 @@ do {\
dot11MeshHWMPRootMode, mask,
NL80211_MESHCONF_HWMP_ROOTMODE,
nla_get_u8);
-
if (mask_out)
*mask_out = mask;
+
return 0;
#undef FILL_IN_MESH_PARAM_IF_SET
}
+static int nl80211_parse_mesh_setup(struct genl_info *info,
+ struct mesh_setup *setup)
+{
+ struct nlattr *tb[NL80211_MESH_SETUP_ATTR_MAX + 1];
+
+ if (!info->attrs[NL80211_ATTR_MESH_SETUP])
+ return -EINVAL;
+ if (nla_parse_nested(tb, NL80211_MESH_SETUP_ATTR_MAX,
+ info->attrs[NL80211_ATTR_MESH_SETUP],
+ nl80211_mesh_setup_params_policy))
+ return -EINVAL;
+
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])
+ setup->path_sel_proto =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL])) ?
+ IEEE80211_PATH_PROTOCOL_VENDOR :
+ IEEE80211_PATH_PROTOCOL_HWMP;
+
+ if (tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])
+ setup->path_metric =
+ (nla_get_u8(tb[NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC])) ?
+ IEEE80211_PATH_METRIC_VENDOR :
+ IEEE80211_PATH_METRIC_AIRTIME;
+
+ if (tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE]) {
+ struct nlattr *ieattr =
+ tb[NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE];
+ if (!is_valid_ie_attr(ieattr))
+ return -EINVAL;
+ setup->vendor_ie = nla_data(ieattr);
+ setup->vendor_ie_len = nla_len(ieattr);
+ }
+
+ return 0;
+}
+
static int nl80211_update_mesh_config(struct sk_buff *skb,
struct genl_info *info)
{
@@ -4667,10 +4711,12 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
struct cfg80211_registered_device *rdev = info->user_ptr[0];
struct net_device *dev = info->user_ptr[1];
struct mesh_config cfg;
+ struct mesh_setup setup;
int err;
/* start with default */
memcpy(&cfg, &default_mesh_config, sizeof(cfg));
+ memcpy(&setup, &default_mesh_setup, sizeof(setup));
if (info->attrs[NL80211_ATTR_MESH_CONFIG]) {
/* and parse parameters if given */
@@ -4683,10 +4729,17 @@ static int nl80211_join_mesh(struct sk_buff *skb, struct genl_info *info)
!nla_len(info->attrs[NL80211_ATTR_MESH_ID]))
return -EINVAL;
- return cfg80211_join_mesh(rdev, dev,
- nla_data(info->attrs[NL80211_ATTR_MESH_ID]),
- nla_len(info->attrs[NL80211_ATTR_MESH_ID]),
- &cfg);
+ setup.mesh_id = nla_data(info->attrs[NL80211_ATTR_MESH_ID]);
+ setup.mesh_id_len = nla_len(info->attrs[NL80211_ATTR_MESH_ID]);
+
+ if (info->attrs[NL80211_ATTR_MESH_SETUP]) {
+ /* parse additional setup parameters if given */
+ err = nl80211_parse_mesh_setup(info, &setup);
+ if (err)
+ return err;
+ }
+
+ return cfg80211_join_mesh(rdev, dev, &setup, &cfg);
}
static int nl80211_leave_mesh(struct sk_buff *skb, struct genl_info *info)