From: Luis Carlos Cobo <luisca@cozybit.com>
To: linux-wireless@vger.kernel.org
Subject: [PATCH 3/4] o80211s: (mac80211s) basic mesh interface support
Date: Fri, 9 Nov 2007 17:22:39 -0800 [thread overview]
Message-ID: <473516ee.26d7720a.3f57.ffff8233@mx.google.com> (raw)
This commit introduces mesh interface support for the mac80211 stack. It
provides support for pre-802.11s frame format, static unicast forwarding, mesh
beaconing, mesh statistics through debugfs.
Signed-off-by: Luis Carlos Cobo <luisca@cozybit.com>
---
include/net/mac80211.h | 3 +
net/mac80211/Makefile | 1 +
net/mac80211/cfg.c | 27 +++++++
net/mac80211/debugfs_netdev.c | 36 +++++++++
net/mac80211/ieee80211.c | 17 ++++-
net/mac80211/ieee80211_i.h | 18 ++++-
net/mac80211/ieee80211_iface.c | 11 +++
net/mac80211/ieee80211_sta.c | 120 ++++++++++++++++++++++++++++-
net/mac80211/ieee80211s.h | 50 ++++++++++++
net/mac80211/mesh_pathtbl.c | 169 ++++++++++++++++++++++++++++++++++++++++
net/mac80211/rx.c | 66 +++++++++++++++-
net/mac80211/tx.c | 38 +++++++++-
net/mac80211/util.c | 41 ++++++++++
13 files changed, 590 insertions(+), 7 deletions(-)
create mode 100644 net/mac80211/ieee80211s.h
create mode 100644 net/mac80211/mesh_pathtbl.c
diff --git a/include/net/mac80211.h b/include/net/mac80211.h
index 17b6039..14123df 100644
--- a/include/net/mac80211.h
+++ b/include/net/mac80211.h
@@ -474,6 +474,7 @@ enum ieee80211_if_types {
IEEE80211_IF_TYPE_AP,
IEEE80211_IF_TYPE_STA,
IEEE80211_IF_TYPE_IBSS,
+ IEEE80211_IF_TYPE_MESH,
IEEE80211_IF_TYPE_MNTR,
IEEE80211_IF_TYPE_WDS,
IEEE80211_IF_TYPE_VLAN,
@@ -521,6 +522,8 @@ struct ieee80211_if_init_conf {
* config_interface() call, so copy the value somewhere if you need
* it.
* @ssid_len: length of the @ssid field.
+ * @mesh_id: mesh ID of the mesh network we will be part of.
+ * @mesh_id_len: length of the @mesh_id field.
* @beacon: beacon template. Valid only if @host_gen_beacon_template in
* &struct ieee80211_hw is set. The driver is responsible of freeing
* the sk_buff.
diff --git a/net/mac80211/Makefile b/net/mac80211/Makefile
index 1e6237b..fcf80ec 100644
--- a/net/mac80211/Makefile
+++ b/net/mac80211/Makefile
@@ -24,4 +24,5 @@ mac80211-objs := \
key.o \
util.o \
event.o \
+ mesh_pathtbl.o \
$(mac80211-objs-y)
diff --git a/net/mac80211/cfg.c b/net/mac80211/cfg.c
index 9e2bc1f..24f8086 100644
--- a/net/mac80211/cfg.c
+++ b/net/mac80211/cfg.c
@@ -25,6 +25,8 @@ nl80211_type_to_mac80211_type(enum nl80211_iftype type)
return IEEE80211_IF_TYPE_STA;
case NL80211_IFTYPE_MONITOR:
return IEEE80211_IF_TYPE_MNTR;
+ case NL80211_IFTYPE_MESH:
+ return IEEE80211_IF_TYPE_MESH;
default:
return IEEE80211_IF_TYPE_INVALID;
}
@@ -99,8 +101,33 @@ static int ieee80211_change_iface(struct wiphy *wiphy, int ifindex,
return 0;
}
+static int ieee80211_if_set_mesh_cfg(struct wiphy *wiphy,
+ struct net_device *dev, struct mesh_params *params)
+{
+ struct ieee80211_local *local = wiphy_priv(wiphy);
+ struct ieee80211_if_sta *ifsta;
+ struct ieee80211_sub_if_data *sdata = NULL;
+ if (unlikely(local->reg_state != IEEE80211_DEV_REGISTERED))
+ return -ENODEV;
+
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ if (sdata->type != IEEE80211_IF_TYPE_MESH)
+ return -EINVAL;
+
+ ifsta = &sdata->u.sta;
+ ifsta->mesh_id_len = params->mesh_id_len;
+ if (params->mesh_id_len)
+ memcpy(ifsta->mesh_id, params->mesh_id, params->mesh_id_len);
+
+ /* If the iface is down, it will be configure when it is opened */
+ if (netif_running(dev))
+ ieee80211_if_config(dev);
+ return 0;
+}
+
struct cfg80211_ops mac80211_config_ops = {
.add_virtual_intf = ieee80211_add_iface,
.del_virtual_intf = ieee80211_del_iface,
.change_virtual_intf = ieee80211_change_iface,
+ .set_mesh_cfg = ieee80211_if_set_mesh_cfg,
};
diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c
index f0e6ab7..e93b6bd 100644
--- a/net/mac80211/debugfs_netdev.c
+++ b/net/mac80211/debugfs_netdev.c
@@ -162,6 +162,10 @@ __IEEE80211_IF_FILE(beacon_tail_len);
/* WDS attributes */
IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC);
+/* Mesh stats attributes */
+IEEE80211_IF_FILE(fwded_frames, u.sta.mshstats.fwded_frames, DEC);
+IEEE80211_IF_FILE(dropped_frames_ttl, u.sta.mshstats.dropped_frames_ttl, DEC);
+
#define DEBUGFS_ADD(name, type)\
sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\
sdata->debugfsdir, sdata, &name##_ops);
@@ -226,12 +230,27 @@ static void add_monitor_files(struct ieee80211_sub_if_data *sdata)
{
}
+#define MESHSTATS_ADD(name)\
+ sdata->mesh_stats.name = debugfs_create_file(#name, 0444,\
+ sdata->mesh_stats_dir, sdata, &name##_ops);
+
+static void add_mesh_stats(struct ieee80211_sub_if_data *sdata)
+{
+ sdata->mesh_stats_dir = debugfs_create_dir("mesh_stats",
+ sdata->debugfsdir);
+ MESHSTATS_ADD(fwded_frames);
+ MESHSTATS_ADD(dropped_frames_ttl);
+}
+
static void add_files(struct ieee80211_sub_if_data *sdata)
{
if (!sdata->debugfsdir)
return;
switch (sdata->type) {
+ case IEEE80211_IF_TYPE_MESH:
+ add_mesh_stats(sdata);
+ /* fall through */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
add_sta_files(sdata);
@@ -319,12 +338,29 @@ static void del_monitor_files(struct ieee80211_sub_if_data *sdata)
{
}
+#define MESHSTATS_DEL(name) \
+ do { \
+ debugfs_remove(sdata->mesh_stats.name); \
+ sdata->mesh_stats.name = NULL; \
+ } while (0)
+
+static void del_mesh_stats(struct ieee80211_sub_if_data *sdata)
+{
+ MESHSTATS_DEL(fwded_frames);
+ MESHSTATS_DEL(dropped_frames_ttl);
+ debugfs_remove(sdata->mesh_stats_dir);
+ sdata->mesh_stats_dir = NULL;
+}
+
static void del_files(struct ieee80211_sub_if_data *sdata, int type)
{
if (!sdata->debugfsdir)
return;
switch (type) {
+ case IEEE80211_IF_TYPE_MESH:
+ del_mesh_stats(sdata);
+ /* fall through */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
del_sta_files(sdata);
diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c
index e0ee65a..1c9cf22 100644
--- a/net/mac80211/ieee80211.c
+++ b/net/mac80211/ieee80211.c
@@ -26,6 +26,7 @@
#include "ieee80211_i.h"
#include "ieee80211_rate.h"
+#include "ieee80211s.h"
#include "wep.h"
#include "wme.h"
#include "aes_ccm.h"
@@ -126,9 +127,15 @@ static void ieee80211_master_set_multicast_list(struct net_device *dev)
static int ieee80211_change_mtu(struct net_device *dev, int new_mtu)
{
+ int meshhdrlen;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+
+ meshhdrlen = (sdata->type == IEEE80211_IF_TYPE_MESH) ? 5 : 0;
+
/* FIX: what would be proper limits for MTU?
* This interface uses 802.3 frames. */
- if (new_mtu < 256 || new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6) {
+ if (new_mtu < 256 ||
+ new_mtu > IEEE80211_MAX_DATA_LEN - 24 - 6 - meshhdrlen) {
printk(KERN_WARNING "%s: invalid MTU %d\n",
dev->name, new_mtu);
return -EINVAL;
@@ -202,6 +209,7 @@ static int ieee80211_open(struct net_device *dev)
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_MNTR:
case IEEE80211_IF_TYPE_IBSS:
+ case IEEE80211_IF_TYPE_MESH:
/* no special treatment */
break;
case IEEE80211_IF_TYPE_INVALID:
@@ -465,6 +473,11 @@ static int __ieee80211_if_config(struct net_device *dev,
conf.bssid = sdata->u.sta.bssid;
conf.ssid = sdata->u.sta.ssid;
conf.ssid_len = sdata->u.sta.ssid_len;
+ } else if (sdata->type == IEEE80211_IF_TYPE_MESH) {
+ /* SSID is wildcard (all 0s) */
+ ieee80211_set_mesh_beacon_template(dev, &conf);
+ local->hw.flags |= IEEE80211_HW_HOST_GEN_BEACON_TEMPLATE;
+ ieee80211_start_mesh(dev);
} else if (sdata->type == IEEE80211_IF_TYPE_AP) {
conf.ssid = sdata->u.ap.ssid;
conf.ssid_len = sdata->u.ap.ssid_len;
@@ -1262,6 +1275,8 @@ static void __exit ieee80211_exit(void)
ieee80211_rate_control_unregister(&mac80211_rcsimple);
#endif
+ if (mesh_allocated)
+ mesh_pathtbl_unregister();
ieee80211_wme_unregister();
ieee80211_debugfs_netdev_exit();
}
diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h
index b4e32ab..c25de0e 100644
--- a/net/mac80211/ieee80211_i.h
+++ b/net/mac80211/ieee80211_i.h
@@ -25,6 +25,7 @@
#include <net/wireless.h>
#include "ieee80211_key.h"
#include "sta_info.h"
+#include "ieee80211s.h"
/* ieee80211.o internal definitions, etc. These are not included into
* low-level drivers. */
@@ -235,13 +236,16 @@ struct ieee80211_if_sta {
enum {
IEEE80211_DISABLED, IEEE80211_AUTHENTICATE,
IEEE80211_ASSOCIATE, IEEE80211_ASSOCIATED,
- IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED
+ IEEE80211_IBSS_SEARCH, IEEE80211_IBSS_JOINED,
+ IEEE80211_MESH
} state;
struct timer_list timer;
struct work_struct work;
u8 bssid[ETH_ALEN], prev_bssid[ETH_ALEN];
u8 ssid[IEEE80211_MAX_SSID_LEN];
size_t ssid_len;
+ u8 mesh_id[IEEE80211_MAX_MESH_ID_LEN];
+ size_t mesh_id_len;
u16 aid;
u16 ap_capab, capab;
u8 *extra_ie; /* to be added to the end of AssocReq */
@@ -274,6 +278,8 @@ struct ieee80211_if_sta {
u32 supp_rates_bits;
int wmm_last_param_set;
+ struct mesh_stats mshstats;
+ atomic_t mesh_seqnum;
};
@@ -384,6 +390,12 @@ struct ieee80211_sub_if_data {
} monitor;
struct dentry *default_key;
} debugfs;
+
+ struct dentry *mesh_stats_dir;
+ struct {
+ struct dentry *fwded_frames;
+ struct dentry *dropped_frames_ttl;
+ } mesh_stats;
#endif
};
@@ -759,6 +771,10 @@ int ieee80211_sta_deauthenticate(struct net_device *dev, u16 reason);
int ieee80211_sta_disassociate(struct net_device *dev, u16 reason);
void ieee80211_erp_info_change_notify(struct net_device *dev, u8 changes);
void ieee80211_reset_erp_info(struct net_device *dev);
+void ieee80211_start_mesh(struct net_device *dev);
+int ieee80211_set_mesh_beacon_template(struct net_device *dev,
+ struct ieee80211_if_conf *conf);
+
/* ieee80211_iface.c */
int ieee80211_if_add(struct net_device *dev, const char *name,
diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c
index 43e505d..b9f02f5 100644
--- a/net/mac80211/ieee80211_iface.c
+++ b/net/mac80211/ieee80211_iface.c
@@ -15,6 +15,7 @@
#include "ieee80211_i.h"
#include "sta_info.h"
#include "debugfs_netdev.h"
+#include "ieee80211s.h"
void ieee80211_if_sdata_init(struct ieee80211_sub_if_data *sdata)
{
@@ -134,6 +135,15 @@ void ieee80211_if_set_type(struct net_device *dev, int type)
sdata->bss = &sdata->u.ap;
INIT_LIST_HEAD(&sdata->u.ap.vlans);
break;
+ case IEEE80211_IF_TYPE_MESH:
+ if (!mesh_allocated) {
+ /* Allocate all mesh structures when creating the first
+ * mesh interface.
+ */
+ mesh_pathtbl_init();
+ mesh_allocated = 1;
+ }
+ /* fall thru */
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS: {
struct ieee80211_sub_if_data *msdata;
@@ -232,6 +242,7 @@ void ieee80211_if_reinit(struct net_device *dev)
break;
case IEEE80211_IF_TYPE_STA:
case IEEE80211_IF_TYPE_IBSS:
+ case IEEE80211_IF_TYPE_MESH:
kfree(sdata->u.sta.extra_ie);
sdata->u.sta.extra_ie = NULL;
kfree(sdata->u.sta.assocreq_ies);
diff --git a/net/mac80211/ieee80211_sta.c b/net/mac80211/ieee80211_sta.c
index 2079e98..89fb06e 100644
--- a/net/mac80211/ieee80211_sta.c
+++ b/net/mac80211/ieee80211_sta.c
@@ -37,6 +37,7 @@
#define IEEE80211_ASSOC_TIMEOUT (HZ / 5)
#define IEEE80211_ASSOC_MAX_TRIES 3
#define IEEE80211_MONITORING_INTERVAL (2 * HZ)
+#define IEEE80211_MESH_EXPIRATION_INTERVAL (2 * HZ)
#define IEEE80211_PROBE_INTERVAL (60 * HZ)
#define IEEE80211_RETRY_AUTH_INTERVAL (1 * HZ)
#define IEEE80211_SCAN_INTERVAL (2 * HZ)
@@ -759,6 +760,26 @@ static void ieee80211_associate(struct net_device *dev,
}
+static void ieee80211_mesh_housekeeping(struct net_device *dev,
+ struct ieee80211_if_sta *ifsta)
+{
+ /* TODO: check for expired neighbors, paths and frames to non resolved
+ * hw addresses
+ */
+ mod_timer(&ifsta->timer, jiffies + IEEE80211_MESH_EXPIRATION_INTERVAL);
+}
+
+
+void ieee80211_start_mesh(struct net_device *dev)
+{
+ struct ieee80211_if_sta *ifsta;
+ struct ieee80211_sub_if_data *sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ ifsta = &sdata->u.sta;
+ ifsta->state = IEEE80211_MESH;
+ ieee80211_sta_timer((unsigned long)sdata);
+}
+
+
static void ieee80211_associated(struct net_device *dev,
struct ieee80211_if_sta *ifsta)
{
@@ -1989,7 +2010,8 @@ void ieee80211_sta_work(struct work_struct *work)
return;
if (sdata->type != IEEE80211_IF_TYPE_STA &&
- sdata->type != IEEE80211_IF_TYPE_IBSS) {
+ sdata->type != IEEE80211_IF_TYPE_IBSS &&
+ sdata->type != IEEE80211_IF_TYPE_MESH) {
printk(KERN_DEBUG "%s: ieee80211_sta_work: non-STA interface "
"(type=%d)\n", dev->name, sdata->type);
return;
@@ -2031,6 +2053,9 @@ void ieee80211_sta_work(struct work_struct *work)
case IEEE80211_IBSS_JOINED:
ieee80211_sta_merge_ibss(dev, ifsta);
break;
+ case IEEE80211_MESH:
+ ieee80211_mesh_housekeeping(dev, ifsta);
+ break;
default:
printk(KERN_DEBUG "ieee80211_sta_work: Unknown state %d\n",
ifsta->state);
@@ -2196,6 +2221,96 @@ static int ieee80211_sta_config_auth(struct net_device *dev,
return -1;
}
+
+int ieee80211_set_mesh_beacon_template(struct net_device *dev,
+ struct ieee80211_if_conf *conf)
+{
+ struct ieee80211_local *local = wdev_priv(dev->ieee80211_ptr);
+ struct sk_buff *skb = dev_alloc_skb(local->hw.extra_tx_headroom + 400);
+ struct ieee80211_hw_mode *mode = local->oper_hw_mode;
+ struct ieee80211_sub_if_data *sdata;
+ struct ieee80211_mgmt *mgmt;
+ u8 *pos;
+ int rates, i;
+
+ if (!skb)
+ return -1;
+ sdata = IEEE80211_DEV_TO_SUB_IF(dev);
+ skb_reserve(skb, local->hw.extra_tx_headroom);
+ mgmt = (struct ieee80211_mgmt *)
+ skb_put(skb, 24 + sizeof(mgmt->u.beacon));
+ memset(mgmt, 0, 24 + sizeof(mgmt->u.beacon));
+ mgmt->frame_control = IEEE80211_FC(IEEE80211_FTYPE_MGMT,
+ IEEE80211_STYPE_BEACON);
+ memset(mgmt->da, 0xff, ETH_ALEN);
+ memcpy(mgmt->sa, dev->dev_addr, ETH_ALEN);
+ /* SSID is left zeroed, wildcard value */
+ mgmt->u.beacon.beacon_int =
+ cpu_to_le16(local->hw.conf.beacon_int);
+ mgmt->u.beacon.capab_info = 0x0; /* 0x0 for MPs */
+
+ pos = skb_put(skb, 2);
+ *pos++ = WLAN_EID_SSID;
+ *pos++ = 0x0;
+
+ rates = mode->num_rates;
+ if (rates > 8)
+ rates = 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_SUPP_RATES;
+ *pos++ = rates;
+ for (i = 0; i < rates; i++) {
+ int rate = mode->rates[i].rate;
+ *pos++ = (u8) (rate / 5);
+ }
+
+ if (mode->num_rates > 8) {
+ rates = mode->num_rates - 8;
+ pos = skb_put(skb, 2 + rates);
+ *pos++ = WLAN_EID_EXT_SUPP_RATES;
+ *pos++ = rates;
+ for (i = 0; i < rates; i++) {
+ int rate = mode->rates[i+8].rate;
+ *pos++ = (u8) (rate / 5);
+ }
+ }
+
+ pos = skb_put(skb, 17);
+ *pos++ = WLAN_EID_MESH_CONFIG;
+ *pos++ = 15;
+ /* Version */
+ *pos++ = 1;
+
+ /* Active path selection protocol ID */
+ *pos++ = 0x00; /* 3 byte OUI: */
+ *pos++ = 0x0f; /* All other OUIs are */
+ *pos++ = 0xac; /* vendor specific. */
+ *pos++ = 0xff; /* Null protocol. */
+
+ /* Active path selection metric ID */
+ *pos++ = 0x00; /* 3 byte OUI */
+ *pos++ = 0x0f;
+ *pos++ = 0xac;
+ *pos++ = 0xff; /* Null metric */
+ /* Channel precedence:
+ * Not running simple channel unification protocol
+ */
+ memset(pos, 0x00, 4);
+ pos += 4;
+ /* Mesh capability */
+ memset(pos, 0x00, 2);
+
+ pos = skb_put(skb, 2 + sdata->u.sta.mesh_id_len);
+ *pos++ = WLAN_EID_MESH_ID;
+ *pos++ = sdata->u.sta.mesh_id_len;
+ if (sdata->u.sta.mesh_id_len)
+ memcpy(pos, sdata->u.sta.mesh_id, sdata->u.sta.mesh_id_len);
+
+ conf->beacon = skb;
+ return 0;
+}
+
+
static int ieee80211_sta_join_ibss(struct net_device *dev,
struct ieee80211_if_sta *ifsta,
struct ieee80211_sta_bss *bss)
@@ -2674,6 +2789,9 @@ void ieee80211_scan_completed(struct ieee80211_hw *hw)
ieee80211_sta_timer((unsigned long)sdata);
}
+ if (sdata->type == IEEE80211_IF_TYPE_MESH)
+ ieee80211_sta_timer((unsigned long)sdata);
+
netif_wake_queue(sdata->dev);
}
rcu_read_unlock();
diff --git a/net/mac80211/ieee80211s.h b/net/mac80211/ieee80211s.h
new file mode 100644
index 0000000..dcb32be
--- /dev/null
+++ b/net/mac80211/ieee80211s.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2007 open80211s Ltd.
+ * Authors : Luis Carlos Cobo <luisca@cozybit.com>
+ * Javier Cardona <javier@cozybit.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef IEEE80211S_H
+#define IEEE80211S_H
+
+#include <linux/types.h>
+
+extern int mesh_allocated;
+
+/* Data structures */
+struct ieee80211s_hdr {
+ u8 flags;
+ u8 ttl;
+ u8 seqnum[3];
+ u8 eaddr1[6];
+ u8 eaddr2[6];
+ u8 eaddr3[6];
+} __attribute__ ((packed));
+
+struct mesh_stats {
+ __u32 fwded_frames; /* Mesh forwarded frames */
+ __u32 dropped_frames_ttl; /* Not forwarded since mesh_ttl == 0 */
+};
+
+
+/* Public interfaces */
+int ieee80211s_nh_lookup(u8 *dst, u8 *next_hop);
+int ieee80211s_start(void);
+void ieee80211s_stop(void);
+void mesh_pathtbl_init(void);
+void mesh_pathtbl_unregister(void);
+int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr);
+int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
+ struct ieee80211_sub_if_data *sdata);
+
+/* Constants */
+#define DEFAULT_IEEE80211S_TTL 5
+
+/* Mesh Header Flags */
+#define IEEE80211S_FLAGS_AE 0x3
+
+#endif /* IEEE80211S_H */
diff --git a/net/mac80211/mesh_pathtbl.c b/net/mac80211/mesh_pathtbl.c
new file mode 100644
index 0000000..1339ab4
--- /dev/null
+++ b/net/mac80211/mesh_pathtbl.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 2007 open80211s Ltd.
+ * Authors : Luis Carlos Cobo <luisca@cozybit.com>
+ * Javier Cardona <javier@cozybit.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/etherdevice.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <net/rtnetlink.h>
+
+static LIST_HEAD(mesh_paths);
+static DEFINE_RWLOCK(mesh_paths_lock);
+
+struct mesh_path {
+ struct list_head list;
+
+ u8 dst[ETH_ALEN];
+ u8 next_hop[ETH_ALEN];
+ int ifa_index;
+};
+
+static const struct nla_policy mpa_policy[MPA_MAX+1] = {
+ [MPA_DST] = { .len = ETH_ALEN },
+ [MPA_NEXT_HOP] = { .len = ETH_ALEN },
+};
+
+int mesh_allocated;
+
+/**
+ * ieee80211s_nh_lookup - Look up next hop in mesh path table
+ * @dst: destination hardware address (input)
+ * @next_hop: next hop hardware address (output)
+ *
+ * Returns: 0 if next hop was found, nonzero otherwise
+ */
+
+int ieee80211s_nh_lookup(u8 *dst, u8 *next_hop)
+{
+ struct mesh_path *mp;
+
+ if (dst == NULL || next_hop == NULL)
+ return -1;
+
+ read_lock(&mesh_paths_lock);
+ list_for_each_entry(mp, &mesh_paths, list) {
+ if (memcmp(dst, mp->dst, ETH_ALEN) == 0) {
+ memcpy(next_hop, mp->next_hop, ETH_ALEN);
+ read_unlock(&mesh_paths_lock);
+ return 0;
+ }
+ }
+ read_unlock(&mesh_paths_lock);
+ return -1;
+}
+
+
+static int o80211s_newmeshpath(struct sk_buff *skb, struct nlmsghdr *nlh,
+ void *arg)
+{
+ struct mpmsg *mpm;
+ struct nlattr *tb[MPA_MAX+1];
+ struct mesh_path *newpath;
+ int err;
+
+ err = nlmsg_parse(nlh, sizeof(*mpm), tb, MPA_MAX, mpa_policy);
+ if (err < 0)
+ return err;
+
+ mpm = nlmsg_data(nlh);
+ if (!tb[MPA_DST] || !tb[MPA_NEXT_HOP])
+ return -EINVAL;
+
+ newpath = kmalloc(sizeof(struct mesh_path), GFP_KERNEL);
+ newpath->ifa_index = mpm->ifa_index;
+ memcpy(newpath->dst, nla_data(tb[MPA_DST]), ETH_ALEN);
+ memcpy(newpath->next_hop, nla_data(tb[MPA_NEXT_HOP]), ETH_ALEN);
+ write_lock(&mesh_paths_lock);
+ list_add(&newpath->list, &mesh_paths);
+ write_unlock(&mesh_paths_lock);
+ return err;
+}
+
+static int o80211s_delmeshpath(struct sk_buff *skb, struct nlmsghdr *nlh,
+ void *arg)
+{
+ struct list_head *p, *q;
+ struct mesh_path *path;
+ list_for_each_safe(p, q, &mesh_paths) {
+ path = list_entry(p, struct mesh_path, list);
+ list_del(p);
+ kfree(path);
+ }
+ return 0;
+}
+
+static int o80211s_getmeshpath(struct sk_buff *skb, struct nlmsghdr *nlh,
+ void *arg)
+{
+ return 0;
+}
+
+static int o80211s_dumpmeshpaths(struct sk_buff *skb,
+ struct netlink_callback *cb)
+{
+ int idx;
+ int s_idx = cb->args[0];
+ struct mesh_path *mp;
+ struct nlmsghdr *nlh;
+ struct mpmsg *hdr;
+
+ read_lock(&mesh_paths_lock);
+ idx = 0;
+ list_for_each_entry(mp, &mesh_paths, list) {
+ if (idx < s_idx)
+ goto cont;
+ nlh = nlmsg_put(skb, NETLINK_CB(cb->skb).pid,
+ cb->nlh->nlmsg_seq, RTM_NEWMESHPATH,
+ sizeof(*hdr), NLM_F_MULTI);
+ if (nlh == NULL)
+ goto nlh_failure;
+ hdr = nlmsg_data(nlh);
+ hdr->ifa_index = mp->ifa_index;
+ hdr->mpm_flags = 0;
+ NLA_PUT(skb, MPA_DST, ETH_ALEN, mp->dst);
+ NLA_PUT(skb, MPA_NEXT_HOP, ETH_ALEN, mp->next_hop);
+ nlmsg_end(skb, nlh);
+ break;
+cont:
+ idx++;
+ }
+ read_unlock(&mesh_paths_lock);
+ cb->args[0] = idx+1;
+ return skb->len;
+
+nla_put_failure:
+ nlmsg_cancel(skb, nlh);
+nlh_failure:
+ read_unlock(&mesh_paths_lock);
+ return -EMSGSIZE;
+}
+
+void mesh_pathtbl_init(void)
+{
+ rtnl_register(PF_UNSPEC, RTM_NEWMESHPATH, o80211s_newmeshpath, NULL);
+ rtnl_register(PF_UNSPEC, RTM_GETMESHPATH, o80211s_getmeshpath,
+ o80211s_dumpmeshpaths);
+ rtnl_register(PF_UNSPEC, RTM_DELMESHPATH, o80211s_delmeshpath, NULL);
+}
+
+void mesh_pathtbl_unregister(void)
+{
+ struct list_head *p, *q;
+ struct mesh_path *path;
+
+ rtnl_unregister(PF_UNSPEC, RTM_NEWMESHPATH);
+ rtnl_unregister(PF_UNSPEC, RTM_DELMESHPATH);
+ rtnl_unregister(PF_UNSPEC, RTM_GETMESHPATH);
+ list_for_each_safe(p, q, &mesh_paths) {
+ path = list_entry(p, struct mesh_path, list);
+ list_del(p);
+ kfree(path);
+ }
+}
diff --git a/net/mac80211/rx.c b/net/mac80211/rx.c
index 428a9fc..7ca8637 100644
--- a/net/mac80211/rx.c
+++ b/net/mac80211/rx.c
@@ -19,6 +19,7 @@
#include "ieee80211_i.h"
#include "ieee80211_led.h"
+#include "ieee80211s.h"
#include "wep.h"
#include "wpa.h"
#include "tkip.h"
@@ -397,6 +398,16 @@ ieee80211_rx_h_check(struct ieee80211_txrx_data *rx)
* deauth/disassoc frames when needed. In addition, hostapd is
* responsible for filtering on both auth and assoc states.
*/
+
+ if (rx->sdata->type == IEEE80211_IF_TYPE_MESH) {
+ if (((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA) &&
+ !((rx->fc & IEEE80211_FCTL_FROMDS) &&
+ (rx->fc & IEEE80211_FCTL_TODS)))
+ return TXRX_DROP;
+ else
+ return TXRX_CONTINUE;
+ }
+
if (unlikely(((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_DATA ||
((rx->fc & IEEE80211_FCTL_FTYPE) == IEEE80211_FTYPE_CTL &&
(rx->fc & IEEE80211_FCTL_STYPE) == IEEE80211_STYPE_PSPOLL)) &&
@@ -1033,6 +1044,18 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
hdrlen = ieee80211_get_hdrlen(fc);
+ if (sdata->type == IEEE80211_IF_TYPE_MESH) {
+ int meshhdrlen = ieee80211_get_mesh_hdrlen(
+ (struct ieee80211s_hdr *) skb->data);
+ /* Copy mesh header on cb to be used if forwarded.
+ * It will be used as mesh header template at
+ * tx.c:ieee80211_subif_start_xmit() if interface
+ * type is mesh and skb->pkt_type == PACKET_OTHERHOST
+ */
+ memcpy(skb->cb, skb->data + hdrlen, meshhdrlen);
+ hdrlen += meshhdrlen;
+ }
+
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
* IEEE 802.11 address fields:
@@ -1066,7 +1089,8 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
memcpy(dst, hdr->addr3, ETH_ALEN);
memcpy(src, hdr->addr4, ETH_ALEN);
- if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS)) {
+ if (unlikely(sdata->type != IEEE80211_IF_TYPE_WDS &&
+ sdata->type != IEEE80211_IF_TYPE_MESH)) {
if (net_ratelimit())
printk(KERN_DEBUG "%s: dropped FromDS&ToDS "
"frame (RA=%s TA=%s DA=%s SA=%s)\n",
@@ -1174,6 +1198,33 @@ ieee80211_rx_h_data(struct ieee80211_txrx_data *rx)
}
}
+ /* Mesh forwarding */
+ if (sdata->type == IEEE80211_IF_TYPE_MESH) {
+ u8 *mesh_ttl = &((struct ieee80211s_hdr *)skb->cb)->ttl;
+ (*mesh_ttl)--;
+ if (is_multicast_ether_addr(dst)) {
+ /*
+ * Disabled to avoid traffic explosion till we have
+ * RBT and neighbor filtering
+ *
+ if (*mesh_ttl > 0)
+ skb2 = skb_copy(skb, GFP_ATOMIC);
+ skb2->pkt_type = PACKET_OTHERHOST;
+ */
+ } else if (skb->pkt_type != PACKET_OTHERHOST &&
+ compare_ether_addr(sdata->dev->dev_addr, dst) != 0) {
+ if (*mesh_ttl == 0) {
+ sdata->u.sta.mshstats.dropped_frames_ttl++;
+ return TXRX_DROP;
+ }
+ skb2 = skb;
+ skb2->pkt_type = PACKET_OTHERHOST;
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ skb = NULL;
+ sdata->u.sta.mshstats.fwded_frames++;
+ }
+ }
+
if (skb) {
/* deliver to local stack */
skb->protocol = eth_type_trans(skb, dev);
@@ -1202,7 +1253,8 @@ ieee80211_rx_h_mgmt(struct ieee80211_txrx_data *rx)
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
if ((sdata->type == IEEE80211_IF_TYPE_STA ||
- sdata->type == IEEE80211_IF_TYPE_IBSS) &&
+ sdata->type == IEEE80211_IF_TYPE_IBSS ||
+ sdata->type == IEEE80211_IF_TYPE_MESH) &&
!(sdata->flags & IEEE80211_SDATA_USERSPACE_MLME))
ieee80211_sta_rx_mgmt(rx->dev, rx->skb, rx->u.rx.status);
else
@@ -1389,6 +1441,16 @@ static int prepare_for_handlers(struct ieee80211_sub_if_data *sdata,
rx->sta = ieee80211_ibss_add_sta(sdata->dev, rx->skb,
bssid, hdr->addr2);
break;
+
+ case IEEE80211_IF_TYPE_MESH:
+ if (!multicast &&
+ compare_ether_addr(sdata->dev->dev_addr,
+ hdr->addr1) != 0) {
+ if (!(sdata->dev->flags & IFF_PROMISC))
+ return 0;
+ rx->flags &= ~IEEE80211_TXRXD_RXRA_MATCH;
+ }
+ break;
case IEEE80211_IF_TYPE_VLAN:
case IEEE80211_IF_TYPE_AP:
if (!bssid) {
diff --git a/net/mac80211/tx.c b/net/mac80211/tx.c
index 1a53154..0aba117 100644
--- a/net/mac80211/tx.c
+++ b/net/mac80211/tx.c
@@ -26,6 +26,7 @@
#include "ieee80211_i.h"
#include "ieee80211_led.h"
+#include "ieee80211s.h"
#include "wep.h"
#include "wpa.h"
#include "wme.h"
@@ -230,6 +231,9 @@ ieee80211_tx_h_check_assoc(struct ieee80211_txrx_data *tx)
(tx->fc & IEEE80211_FCTL_STYPE) != IEEE80211_STYPE_PROBE_REQ))
return TXRX_DROP;
+ if (tx->sdata->type == IEEE80211_IF_TYPE_MESH)
+ return TXRX_CONTINUE;
+
if (tx->flags & IEEE80211_TXRXD_TXPS_BUFFERED)
return TXRX_CONTINUE;
@@ -1342,8 +1346,9 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
struct ieee80211_tx_packet_data *pkt_data;
struct ieee80211_sub_if_data *sdata;
int ret = 1, head_need;
- u16 ethertype, hdrlen, fc;
+ u16 ethertype, hdrlen, meshhdrlen = 0, fc;
struct ieee80211_hdr hdr;
+ struct ieee80211s_hdr mesh_hdr;
const u8 *encaps_data;
int encaps_len, skip_header_bytes;
int nh_pos, h_pos;
@@ -1385,6 +1390,29 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
hdrlen = 30;
break;
+ case IEEE80211_IF_TYPE_MESH:
+ fc |= IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS;
+ /* RA TA DA SA */
+ if (is_multicast_ether_addr(skb->data))
+ memcpy(hdr.addr1, skb->data, ETH_ALEN);
+ else if (ieee80211s_nh_lookup(skb->data, hdr.addr1)) {
+ ret = 0;
+ goto fail;
+ }
+ memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
+ memcpy(hdr.addr3, skb->data, ETH_ALEN);
+ memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
+ if (skb->pkt_type == PACKET_OTHERHOST) {
+ /* Forwarded frame, keep mesh ttl and seqnum */
+ struct ieee80211s_hdr *prev_meshhdr;
+ prev_meshhdr = ((struct ieee80211s_hdr *)skb->cb);
+ meshhdrlen = ieee80211_get_mesh_hdrlen(prev_meshhdr);
+ memcpy(&mesh_hdr, prev_meshhdr, meshhdrlen);
+ } else
+ meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr,
+ sdata);
+ hdrlen = 30;
+ break;
case IEEE80211_IF_TYPE_STA:
fc |= IEEE80211_FCTL_TODS;
/* BSSID SA DA */
@@ -1450,7 +1478,7 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
* build in headroom in __dev_alloc_skb() (linux/skbuff.h) and
* alloc_skb() (net/core/skbuff.c)
*/
- head_need = hdrlen + encaps_len + local->tx_headroom;
+ head_need = hdrlen + encaps_len + meshhdrlen + local->tx_headroom;
head_need -= skb_headroom(skb);
/* We are going to modify skb data, so make a copy of it if happens to
@@ -1484,6 +1512,12 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
h_pos += encaps_len;
}
+ if (meshhdrlen > 0) {
+ memcpy(skb_push(skb, meshhdrlen), &mesh_hdr, meshhdrlen);
+ nh_pos += meshhdrlen;
+ h_pos += meshhdrlen;
+ }
+
if (fc & IEEE80211_STYPE_QOS_DATA) {
__le16 *qos_control;
diff --git a/net/mac80211/util.c b/net/mac80211/util.c
index 5a0564e..a850685 100644
--- a/net/mac80211/util.c
+++ b/net/mac80211/util.c
@@ -217,6 +217,25 @@ int ieee80211_get_hdrlen_from_skb(const struct sk_buff *skb)
}
EXPORT_SYMBOL(ieee80211_get_hdrlen_from_skb);
+int ieee80211_get_mesh_hdrlen(struct ieee80211s_hdr *meshhdr)
+{
+ int ae = meshhdr->flags & IEEE80211S_FLAGS_AE;
+ /* 7.1.3.5a.2 */
+ switch (ae) {
+ case 0:
+ return 5;
+ case 1:
+ return 11;
+ case 2:
+ return 17;
+ case 3:
+ return 23;
+ default:
+ return 5;
+ }
+}
+EXPORT_SYMBOL(ieee80211_get_mesh_hdrlen);
+
int ieee80211_is_eapol(const struct sk_buff *skb)
{
const struct ieee80211_hdr *hdr;
@@ -484,3 +503,25 @@ void ieee80211_wake_queues(struct ieee80211_hw *hw)
ieee80211_wake_queue(hw, i);
}
EXPORT_SYMBOL(ieee80211_wake_queues);
+
+/**
+ * ieee80211_new_mesh_header - create a new mesh header
+ * @meshhdr: uninitialized mesh header
+ * @sdata: mesh interface to be used
+ *
+ * Return the header length.
+ */
+int ieee80211_new_mesh_header(struct ieee80211s_hdr *meshhdr,
+ struct ieee80211_sub_if_data *sdata)
+{
+ /* TODO: create a per mesh interface atomic sequence counter */
+ int mesh_seqnum = atomic_inc_return(&sdata->u.sta.mesh_seqnum);
+
+ meshhdr->flags = 0;
+ meshhdr->ttl = DEFAULT_IEEE80211S_TTL;
+ meshhdr->seqnum[0] = (u8) (mesh_seqnum & 0xff);
+ meshhdr->seqnum[1] = (u8) ((mesh_seqnum >> 8) & 0xff);
+ meshhdr->seqnum[2] = (u8) ((mesh_seqnum >> 16) & 0xff);
+ return 5;
+}
+EXPORT_SYMBOL(ieee80211_new_mesh_header);
--
1.5.2.5
next reply other threads:[~2007-11-10 2:26 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-11-10 1:22 Luis Carlos Cobo [this message]
2007-11-10 10:11 ` [PATCH 3/4] o80211s: (mac80211s) basic mesh interface support Johannes Berg
2007-11-11 0:03 ` Javier Cardona
2007-11-12 11:14 ` Johannes Berg
2007-11-13 20:31 ` Javier Cardona
2007-11-13 20:36 ` Dan Williams
2007-11-14 0:39 ` Guido R. Hiertz
2007-11-14 0:11 ` Guido R. Hiertz
2007-11-14 12:42 ` Johannes Berg
2007-11-12 23:17 ` Luis Carlos Cobo
2007-11-13 13:18 ` Johannes Berg
2007-11-16 0:13 ` Johannes Berg
2007-11-20 20:06 ` Luis Carlos Cobo
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=473516ee.26d7720a.3f57.ffff8233@mx.google.com \
--to=luisca@cozybit.com \
--cc=linux-wireless@vger.kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.