2010-12-03 11:20:44 +03:00
# include <linux/ieee80211.h>
2011-07-15 19:47:34 +04:00
# include <linux/export.h>
2010-12-03 11:20:44 +03:00
# include <net/cfg80211.h>
2011-04-08 02:08:34 +04:00
# include "nl80211.h"
2010-12-03 11:20:44 +03:00
# include "core.h"
/* Default values, timeouts in ms */
# define MESH_TTL 31
# define MESH_DEFAULT_ELEMENT_TTL 31
# define MESH_MAX_RETR 3
# define MESH_RET_T 100
# define MESH_CONF_T 100
# define MESH_HOLD_T 100
# define MESH_PATH_TIMEOUT 5000
2011-08-10 03:45:10 +04:00
# define MESH_RANN_INTERVAL 5000
2012-06-13 22:06:06 +04:00
# define MESH_PATH_TO_ROOT_TIMEOUT 6000
# define MESH_ROOT_INTERVAL 5000
2012-06-13 22:06:10 +04:00
# define MESH_ROOT_CONFIRMATION_INTERVAL 2000
2010-12-03 11:20:44 +03:00
/*
* Minimum interval between two consecutive PREQs originated by the same
* interface
*/
# define MESH_PREQ_MIN_INT 10
2011-11-25 05:15:24 +04:00
# define MESH_PERR_MIN_INT 100
2010-12-03 11:20:44 +03:00
# define MESH_DIAM_TRAVERSAL_TIME 50
2012-02-29 05:04:08 +04:00
# define MESH_RSSI_THRESHOLD 0
2010-12-03 11:20:44 +03:00
/*
* A path will be refreshed if it is used PATH_REFRESH_TIME milliseconds
* before timing out . This way it will remain ACTIVE and no data frames
* will be unnecessarily held in the pending queue .
*/
# define MESH_PATH_REFRESH_TIME 1000
# define MESH_MIN_DISCOVERY_TIMEOUT (2 * MESH_DIAM_TRAVERSAL_TIME)
/* Default maximum number of established plinks per interface */
# define MESH_MAX_ESTAB_PLINKS 32
# define MESH_MAX_PREQ_RETRIES 4
2012-03-31 22:31:33 +04:00
# define MESH_SYNC_NEIGHBOR_OFFSET_MAX 50
2010-12-03 11:20:44 +03:00
const struct mesh_config default_mesh_config = {
. dot11MeshRetryTimeout = MESH_RET_T ,
. dot11MeshConfirmTimeout = MESH_CONF_T ,
. dot11MeshHoldingTimeout = MESH_HOLD_T ,
. dot11MeshMaxRetries = MESH_MAX_RETR ,
. dot11MeshTTL = MESH_TTL ,
. element_ttl = MESH_DEFAULT_ELEMENT_TTL ,
. auto_open_plinks = true ,
. dot11MeshMaxPeerLinks = MESH_MAX_ESTAB_PLINKS ,
2012-03-31 22:31:33 +04:00
. dot11MeshNbrOffsetMaxNeighbor = MESH_SYNC_NEIGHBOR_OFFSET_MAX ,
2010-12-03 11:20:44 +03:00
. dot11MeshHWMPactivePathTimeout = MESH_PATH_TIMEOUT ,
. dot11MeshHWMPpreqMinInterval = MESH_PREQ_MIN_INT ,
2011-11-25 05:15:24 +04:00
. dot11MeshHWMPperrMinInterval = MESH_PERR_MIN_INT ,
2010-12-03 11:20:44 +03:00
. dot11MeshHWMPnetDiameterTraversalTime = MESH_DIAM_TRAVERSAL_TIME ,
. dot11MeshHWMPmaxPREQretries = MESH_MAX_PREQ_RETRIES ,
. path_refresh_time = MESH_PATH_REFRESH_TIME ,
. min_discovery_timeout = MESH_MIN_DISCOVERY_TIMEOUT ,
2011-08-10 03:45:10 +04:00
. dot11MeshHWMPRannInterval = MESH_RANN_INTERVAL ,
2011-08-10 03:45:11 +04:00
. dot11MeshGateAnnouncementProtocol = false ,
2012-01-20 21:02:16 +04:00
. dot11MeshForwarding = true ,
2012-02-29 05:04:08 +04:00
. rssi_threshold = MESH_RSSI_THRESHOLD ,
2012-05-01 01:20:32 +04:00
. ht_opmode = IEEE80211_HT_OP_MODE_PROTECTION_NONHT_MIXED ,
2012-06-13 22:06:06 +04:00
. dot11MeshHWMPactivePathToRootTimeout = MESH_PATH_TO_ROOT_TIMEOUT ,
. dot11MeshHWMProotInterval = MESH_ROOT_INTERVAL ,
2012-06-13 22:06:10 +04:00
. dot11MeshHWMPconfirmationInterval = MESH_ROOT_CONFIRMATION_INTERVAL ,
2010-12-03 11:20:44 +03:00
} ;
2010-12-17 04:37:49 +03:00
const struct mesh_setup default_mesh_setup = {
2012-05-17 01:50:20 +04:00
/* cfg80211_join_mesh() will pick a channel if needed */
. channel = NULL ,
. channel_type = NL80211_CHAN_NO_HT ,
2012-03-31 22:31:33 +04:00
. sync_method = IEEE80211_SYNC_METHOD_NEIGHBOR_OFFSET ,
2010-12-17 04:37:49 +03:00
. path_sel_proto = IEEE80211_PATH_PROTOCOL_HWMP ,
. path_metric = IEEE80211_PATH_METRIC_AIRTIME ,
2011-04-08 02:08:27 +04:00
. ie = NULL ,
. ie_len = 0 ,
2011-04-08 02:08:29 +04:00
. is_secure = false ,
2010-12-17 04:37:49 +03:00
} ;
2010-12-03 11:20:44 +03:00
int __cfg80211_join_mesh ( struct cfg80211_registered_device * rdev ,
struct net_device * dev ,
2012-05-17 01:50:20 +04:00
struct mesh_setup * setup ,
2010-12-03 11:20:44 +03:00
const struct mesh_config * conf )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
int err ;
BUILD_BUG_ON ( IEEE80211_MAX_SSID_LEN ! = IEEE80211_MAX_MESH_ID_LEN ) ;
ASSERT_WDEV_LOCK ( wdev ) ;
if ( dev - > ieee80211_ptr - > iftype ! = NL80211_IFTYPE_MESH_POINT )
return - EOPNOTSUPP ;
2011-04-08 02:08:28 +04:00
if ( ! ( rdev - > wiphy . flags & WIPHY_FLAG_MESH_AUTH ) & &
setup - > is_secure )
return - EOPNOTSUPP ;
2010-12-03 11:20:44 +03:00
if ( wdev - > mesh_id_len )
return - EALREADY ;
2010-12-17 04:37:49 +03:00
if ( ! setup - > mesh_id_len )
2010-12-03 11:20:44 +03:00
return - EINVAL ;
if ( ! rdev - > ops - > join_mesh )
return - EOPNOTSUPP ;
2012-05-17 01:50:20 +04:00
if ( ! setup - > channel ) {
/* if no channel explicitly given, use preset channel */
setup - > channel = wdev - > preset_chan ;
setup - > channel_type = wdev - > preset_chantype ;
}
if ( ! setup - > channel ) {
/* if we don't have that either, use the first usable channel */
enum ieee80211_band band ;
for ( band = 0 ; band < IEEE80211_NUM_BANDS ; band + + ) {
struct ieee80211_supported_band * sband ;
struct ieee80211_channel * chan ;
int i ;
sband = rdev - > wiphy . bands [ band ] ;
if ( ! sband )
continue ;
for ( i = 0 ; i < sband - > n_channels ; i + + ) {
chan = & sband - > channels [ i ] ;
if ( chan - > flags & ( IEEE80211_CHAN_NO_IBSS |
IEEE80211_CHAN_PASSIVE_SCAN |
IEEE80211_CHAN_DISABLED |
IEEE80211_CHAN_RADAR ) )
continue ;
setup - > channel = chan ;
break ;
}
if ( setup - > channel )
break ;
}
/* no usable channel ... */
if ( ! setup - > channel )
return - EINVAL ;
setup - > channel_type = NL80211_CHAN_NO_HT ;
}
if ( ! cfg80211_can_beacon_sec_chan ( & rdev - > wiphy , setup - > channel ,
setup - > channel_type ) )
return - EINVAL ;
2012-06-29 14:47:08 +04:00
err = cfg80211_can_use_chan ( rdev , wdev , setup - > channel ,
CHAN_MODE_SHARED ) ;
if ( err )
return err ;
2010-12-17 04:37:49 +03:00
err = rdev - > ops - > join_mesh ( & rdev - > wiphy , dev , conf , setup ) ;
2010-12-03 11:20:44 +03:00
if ( ! err ) {
2010-12-17 04:37:49 +03:00
memcpy ( wdev - > ssid , setup - > mesh_id , setup - > mesh_id_len ) ;
wdev - > mesh_id_len = setup - > mesh_id_len ;
2012-06-29 14:46:58 +04:00
wdev - > channel = setup - > channel ;
2010-12-03 11:20:44 +03:00
}
return err ;
}
int cfg80211_join_mesh ( struct cfg80211_registered_device * rdev ,
struct net_device * dev ,
2012-05-17 01:50:20 +04:00
struct mesh_setup * setup ,
2010-12-03 11:20:44 +03:00
const struct mesh_config * conf )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
int err ;
2012-06-29 14:47:08 +04:00
mutex_lock ( & rdev - > devlist_mtx ) ;
2010-12-03 11:20:44 +03:00
wdev_lock ( wdev ) ;
2010-12-17 04:37:49 +03:00
err = __cfg80211_join_mesh ( rdev , dev , setup , conf ) ;
2010-12-03 11:20:44 +03:00
wdev_unlock ( wdev ) ;
2012-06-29 14:47:08 +04:00
mutex_unlock ( & rdev - > devlist_mtx ) ;
2010-12-03 11:20:44 +03:00
return err ;
}
2012-05-17 01:50:20 +04:00
int cfg80211_set_mesh_freq ( struct cfg80211_registered_device * rdev ,
struct wireless_dev * wdev , int freq ,
enum nl80211_channel_type channel_type )
{
struct ieee80211_channel * channel ;
2012-06-29 14:46:58 +04:00
int err ;
2012-05-17 01:50:20 +04:00
2012-06-06 10:18:22 +04:00
channel = rdev_freq_to_chan ( rdev , freq , channel_type ) ;
if ( ! channel | | ! cfg80211_can_beacon_sec_chan ( & rdev - > wiphy ,
channel ,
channel_type ) ) {
return - EINVAL ;
}
2012-05-17 01:50:20 +04:00
/*
* Workaround for libertas ( only ! ) , it puts the interface
* into mesh mode but doesn ' t implement join_mesh . Instead ,
* it is configured via sysfs and then joins the mesh when
* you set the channel . Note that the libertas mesh isn ' t
* compatible with 802.11 mesh .
*/
2012-06-06 10:18:22 +04:00
if ( rdev - > ops - > libertas_set_mesh_channel ) {
if ( channel_type ! = NL80211_CHAN_NO_HT )
return - EINVAL ;
2012-05-17 01:50:20 +04:00
if ( ! netif_running ( wdev - > netdev ) )
return - ENETDOWN ;
2012-06-29 14:46:58 +04:00
2012-06-29 14:47:08 +04:00
err = cfg80211_can_use_chan ( rdev , wdev , channel ,
CHAN_MODE_SHARED ) ;
if ( err )
return err ;
2012-06-29 14:46:58 +04:00
err = rdev - > ops - > libertas_set_mesh_channel ( & rdev - > wiphy ,
wdev - > netdev ,
channel ) ;
if ( ! err )
wdev - > channel = channel ;
return err ;
2012-05-17 01:50:20 +04:00
}
if ( wdev - > mesh_id_len )
return - EBUSY ;
wdev - > preset_chan = channel ;
wdev - > preset_chantype = channel_type ;
return 0 ;
}
2011-04-08 02:08:34 +04:00
void cfg80211_notify_new_peer_candidate ( struct net_device * dev ,
const u8 * macaddr , const u8 * ie , u8 ie_len , gfp_t gfp )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
if ( WARN_ON ( wdev - > iftype ! = NL80211_IFTYPE_MESH_POINT ) )
return ;
nl80211_send_new_peer_candidate ( wiphy_to_dev ( wdev - > wiphy ) , dev ,
macaddr , ie , ie_len , gfp ) ;
}
EXPORT_SYMBOL ( cfg80211_notify_new_peer_candidate ) ;
2010-12-03 11:20:44 +03:00
static int __cfg80211_leave_mesh ( struct cfg80211_registered_device * rdev ,
struct net_device * dev )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
int err ;
ASSERT_WDEV_LOCK ( wdev ) ;
if ( dev - > ieee80211_ptr - > iftype ! = NL80211_IFTYPE_MESH_POINT )
return - EOPNOTSUPP ;
if ( ! rdev - > ops - > leave_mesh )
return - EOPNOTSUPP ;
if ( ! wdev - > mesh_id_len )
return - ENOTCONN ;
err = rdev - > ops - > leave_mesh ( & rdev - > wiphy , dev ) ;
2012-06-29 14:46:58 +04:00
if ( ! err ) {
2010-12-03 11:20:44 +03:00
wdev - > mesh_id_len = 0 ;
2012-06-29 14:46:58 +04:00
wdev - > channel = NULL ;
}
2010-12-03 11:20:44 +03:00
return err ;
}
int cfg80211_leave_mesh ( struct cfg80211_registered_device * rdev ,
struct net_device * dev )
{
struct wireless_dev * wdev = dev - > ieee80211_ptr ;
int err ;
wdev_lock ( wdev ) ;
err = __cfg80211_leave_mesh ( rdev , dev ) ;
wdev_unlock ( wdev ) ;
return err ;
}