mac80211: Let userspace enable and configure vendor specific path selection.
authorJavier Cardona <javier@cozybit.com>
Fri, 17 Dec 2010 01:37:49 +0000 (17:37 -0800)
committerJohn W. Linville <linville@tuxdriver.com>
Mon, 20 Dec 2010 19:46:57 +0000 (14:46 -0500)
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>
12 files changed:
include/linux/ieee80211.h
include/linux/nl80211.h
include/net/cfg80211.h
net/mac80211/cfg.c
net/mac80211/ieee80211_i.h
net/mac80211/mesh.c
net/mac80211/mesh_plink.c
net/mac80211/tx.c
net/wireless/core.c
net/wireless/core.h
net/wireless/mesh.c
net/wireless/nl80211.c

index 7f23545342425936acb2271421e379a9a879bed6..cd681681d2113b4a74ee64d648c5f88d8dd7833e 100644 (file)
@@ -1291,6 +1291,31 @@ enum ieee80211_key_len {
        WLAN_KEY_LEN_AES_CMAC = 16,
 };
 
+/**
+ * enum - mesh path selection protocol identifier
+ *
+ * @IEEE80211_PATH_PROTOCOL_HWMP: the default path selection protocol
+ * @IEEE80211_PATH_PROTOCOL_VENDOR: a vendor specific protocol that will
+ * be specified in a vendor specific information element
+ */
+enum {
+       IEEE80211_PATH_PROTOCOL_HWMP = 0,
+       IEEE80211_PATH_PROTOCOL_VENDOR = 255,
+};
+
+/**
+ * enum - mesh path selection metric identifier
+ *
+ * @IEEE80211_PATH_METRIC_AIRTIME: the default path selection metric
+ * @IEEE80211_PATH_METRIC_VENDOR: a vendor specific metric that will be
+ * specified in a vendor specific information element
+ */
+enum {
+       IEEE80211_PATH_METRIC_AIRTIME = 0,
+       IEEE80211_PATH_METRIC_VENDOR = 255,
+};
+
+
 /*
  * IEEE 802.11-2007 7.3.2.9 Country information element
  *
index 11a1de67b61876e9d49fb960d083c4bfc91f3bda..69eaccac78c481e256a06f5854a6d9b4e1491cf2 100644 (file)
@@ -872,6 +872,9 @@ enum nl80211_commands {
  *     attributes, specifying what a key should be set as default as.
  *     See &enum nl80211_key_default_types.
  *
+ * @NL80211_ATTR_MESH_SETUP: Optional mesh setup parameters.  These cannot be
+ * changed once the mesh is active.
+ *
  * @NL80211_ATTR_MAX: highest attribute number currently defined
  * @__NL80211_ATTR_AFTER_LAST: internal use
  */
@@ -1054,6 +1057,8 @@ enum nl80211_attrs {
 
        NL80211_ATTR_MAX_REMAIN_ON_CHANNEL_DURATION,
 
+       NL80211_ATTR_MESH_SETUP,
+
        /* add attributes here, update the policy in nl80211.c */
 
        __NL80211_ATTR_AFTER_LAST,
@@ -1564,7 +1569,8 @@ enum nl80211_mntr_flags {
 /**
  * enum nl80211_meshconf_params - mesh configuration parameters
  *
- * Mesh configuration parameters
+ * Mesh configuration parameters. These can be changed while the mesh is
+ * active.
  *
  * @__NL80211_MESHCONF_INVALID: internal use
  *
@@ -1587,9 +1593,6 @@ enum nl80211_mntr_flags {
  * @NL80211_MESHCONF_TTL: specifies the value of TTL field set at a source mesh
  * point.
  *
- * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
- * source mesh point for path selection elements.
- *
  * @NL80211_MESHCONF_AUTO_OPEN_PLINKS: whether we should automatically
  * open peer links when we detect compatible mesh peers.
  *
@@ -1616,6 +1619,9 @@ enum nl80211_mntr_flags {
  *
  * @NL80211_MESHCONF_ROOTMODE: whether root mode is enabled or not
  *
+ * @NL80211_MESHCONF_ELEMENT_TTL: specifies the value of TTL field set at a
+ * source mesh point for path selection elements.
+ *
  * @NL80211_MESHCONF_ATTR_MAX: highest possible mesh configuration attribute
  *
  * @__NL80211_MESHCONF_ATTR_AFTER_LAST: internal use
@@ -1643,6 +1649,39 @@ enum nl80211_meshconf_params {
        NL80211_MESHCONF_ATTR_MAX = __NL80211_MESHCONF_ATTR_AFTER_LAST - 1
 };
 
+/**
+ * enum nl80211_mesh_setup_params - mesh setup parameters
+ *
+ * Mesh setup parameters.  These are used to start/join a mesh and cannot be
+ * changed while the mesh is active.
+ *
+ * @__NL80211_MESH_SETUP_INVALID: Internal use
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL: Enable this option to use a
+ * vendor specific path selection algorithm or disable it to use the default
+ * HWMP.
+ *
+ * @NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC: Enable this option to use a
+ * vendor specific path metric or disable it to use the default Airtime
+ * metric.
+ *
+ * @NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE: A vendor specific information
+ * element that vendors will use to identify the path selection methods and
+ * metrics in use.
+ *
+ * @__NL80211_MESH_SETUP_ATTR_AFTER_LAST: Internal use
+ */
+enum nl80211_mesh_setup_params {
+       __NL80211_MESH_SETUP_INVALID,
+       NL80211_MESH_SETUP_ENABLE_VENDOR_PATH_SEL,
+       NL80211_MESH_SETUP_ENABLE_VENDOR_METRIC,
+       NL80211_MESH_SETUP_VENDOR_PATH_SEL_IE,
+
+       /* keep last */
+       __NL80211_MESH_SETUP_ATTR_AFTER_LAST,
+       NL80211_MESH_SETUP_ATTR_MAX = __NL80211_MESH_SETUP_ATTR_AFTER_LAST - 1
+};
+
 /**
  * enum nl80211_txq_attr - TX queue parameter attributes
  * @__NL80211_TXQ_ATTR_INVALID: Attribute number 0 is reserved
index 7283496c2d05b9effcc5b6f17891e9fb8e9e937f..924d603662335e7f4a2d7a5e075e11fe64cdfa43 100644 (file)
@@ -649,12 +649,20 @@ struct mesh_config {
  * struct mesh_setup - 802.11s mesh setup configuration
  * @mesh_id: the mesh ID
  * @mesh_id_len: length of the mesh ID, at least 1 and at most 32 bytes
+ * @path_sel_proto: which path selection protocol to use
+ * @path_metric: which metric to use
+ * @vendor_ie: vendor information elements (optional)
+ * @vendor_ie_len: length of vendor information elements
  *
  * These parameters are fixed when the mesh is created.
  */
 struct mesh_setup {
        const u8 *mesh_id;
        u8 mesh_id_len;
+       u8  path_sel_proto;
+       u8  path_metric;
+       const u8 *vendor_ie;
+       u8 vendor_ie_len;
 };
 
 /**
index 1c94a2ae22ee64202bb6e2a847478365b6cdb145..ae2c7127a8aa7343f775dc63e862bd7631c9ba37 100644 (file)
@@ -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;
index ce58b2a676e28b89173a893dfd8d4dab730697a5..eadaa243a3dab19ba83a61da405102702d926997 100644 (file)
@@ -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;
 
index 63e1188d50626ff8bc77db68663b94439902455d..c326e009389d918be75d3ed0cb4c1552c2a8b82b 100644 (file)
@@ -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)
index 1c91f0f3c3079ba9f6a8d94097d6e637b948cdad..44b53931ba5e0e410def11e3b216f4944e29a9fd 100644 (file)
@@ -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 };
index 157bde993ef5658b62fc6158a40edabd801afd45..f4b1b624ea9ffc4e40a19619665ae9d7a926c50e 100644 (file)
@@ -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;
 
index 79772fcc37bcaf12b71943d962a3071ee54976ef..e9a5f8ca4c2718424bc0fafec5a9b7eabe6d65b1 100644 (file)
@@ -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;
                }
index 743203bb61ac114388fc66e6f61c98f67c19a5bd..26a0a084e16b886910ea637168982baf8318c812 100644 (file)
@@ -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);
index e0b9747fe50aa0ae3d6833f4bffa80404e1d7bf7..73e39c171ffb1a9d27bb011825ecc5cf8f83e24c 100644 (file)
@@ -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;
index 10be9350752ef6615123424e7b1eeeded6e70143..eef89d0b558bf969d02f8d7cb3ad7d007dbe011d 100644 (file)
@@ -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)