mac80211: mesh portal functionality support

Currently the mesh code doesn't support bridging mesh point interfaces
with wired ethernet or AP to construct an MPP or MAP. This patch adds
code to support the "6 address frame format packet" functionality to
mesh point interfaces. Now the mesh network can be used as backhaul
for end to end communication.

Signed-off-by: Li YanBo <dreamfly281@gmail.com>
Signed-off-by: John W. Linville <linville@tuxdriver.com>
This commit is contained in:
YanBo 2008-09-22 13:30:32 +08:00 committed by John W. Linville
parent 31e9ab2b18
commit 79617deeeb
5 changed files with 201 additions and 11 deletions

View File

@ -471,6 +471,11 @@ struct ieee80211s_hdr {
u8 eaddr3[6];
} __attribute__ ((packed));
/* Mesh flags */
#define MESH_FLAGS_AE_A4 0x1
#define MESH_FLAGS_AE_A5_A6 0x2
#define MESH_FLAGS_PS_DEEP 0x4
/**
* struct ieee80211_quiet_ie
*

View File

@ -71,6 +71,7 @@ enum mesh_path_flags {
*/
struct mesh_path {
u8 dst[ETH_ALEN];
u8 mpp[ETH_ALEN]; /* used for MPP or MAP */
struct ieee80211_sub_if_data *sdata;
struct sta_info *next_hop;
struct timer_list timer;
@ -226,6 +227,9 @@ int mesh_nexthop_lookup(struct sk_buff *skb,
void mesh_path_start_discovery(struct ieee80211_sub_if_data *sdata);
struct mesh_path *mesh_path_lookup(u8 *dst,
struct ieee80211_sub_if_data *sdata);
struct mesh_path *mpp_path_lookup(u8 *dst,
struct ieee80211_sub_if_data *sdata);
int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata);
struct mesh_path *mesh_path_lookup_by_idx(int idx,
struct ieee80211_sub_if_data *sdata);
void mesh_path_fix_nexthop(struct mesh_path *mpath, struct sta_info *next_hop);

View File

@ -36,6 +36,7 @@ struct mpath_node {
};
static struct mesh_table *mesh_paths;
static struct mesh_table *mpp_paths; /* Store paths for MPP&MAP */
/* This lock will have the grow table function as writer and add / delete nodes
* as readers. When reading the table (i.e. doing lookups) we are well protected
@ -94,6 +95,34 @@ struct mesh_path *mesh_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
return NULL;
}
struct mesh_path *mpp_path_lookup(u8 *dst, struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath;
struct hlist_node *n;
struct hlist_head *bucket;
struct mesh_table *tbl;
struct mpath_node *node;
tbl = rcu_dereference(mpp_paths);
bucket = &tbl->hash_buckets[mesh_table_hash(dst, sdata, tbl)];
hlist_for_each_entry_rcu(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->sdata == sdata &&
memcmp(dst, mpath->dst, ETH_ALEN) == 0) {
if (MPATH_EXPIRED(mpath)) {
spin_lock_bh(&mpath->state_lock);
if (MPATH_EXPIRED(mpath))
mpath->flags &= ~MESH_PATH_ACTIVE;
spin_unlock_bh(&mpath->state_lock);
}
return mpath;
}
}
return NULL;
}
/**
* mesh_path_lookup_by_idx - look up a path in the mesh path table by its index
* @idx: index
@ -226,6 +255,91 @@ err_path_alloc:
}
int mpp_path_add(u8 *dst, u8 *mpp, struct ieee80211_sub_if_data *sdata)
{
struct mesh_path *mpath, *new_mpath;
struct mpath_node *node, *new_node;
struct hlist_head *bucket;
struct hlist_node *n;
int grow = 0;
int err = 0;
u32 hash_idx;
if (memcmp(dst, sdata->dev->dev_addr, ETH_ALEN) == 0)
/* never add ourselves as neighbours */
return -ENOTSUPP;
if (is_multicast_ether_addr(dst))
return -ENOTSUPP;
err = -ENOMEM;
new_mpath = kzalloc(sizeof(struct mesh_path), GFP_KERNEL);
if (!new_mpath)
goto err_path_alloc;
new_node = kmalloc(sizeof(struct mpath_node), GFP_KERNEL);
if (!new_node)
goto err_node_alloc;
read_lock(&pathtbl_resize_lock);
memcpy(new_mpath->dst, dst, ETH_ALEN);
memcpy(new_mpath->mpp, mpp, ETH_ALEN);
new_mpath->sdata = sdata;
new_mpath->flags = 0;
skb_queue_head_init(&new_mpath->frame_queue);
new_node->mpath = new_mpath;
new_mpath->exp_time = jiffies;
spin_lock_init(&new_mpath->state_lock);
hash_idx = mesh_table_hash(dst, sdata, mpp_paths);
bucket = &mpp_paths->hash_buckets[hash_idx];
spin_lock(&mpp_paths->hashwlock[hash_idx]);
err = -EEXIST;
hlist_for_each_entry(node, n, bucket, list) {
mpath = node->mpath;
if (mpath->sdata == sdata && memcmp(dst, mpath->dst, ETH_ALEN) == 0)
goto err_exists;
}
hlist_add_head_rcu(&new_node->list, bucket);
if (atomic_inc_return(&mpp_paths->entries) >=
mpp_paths->mean_chain_len * (mpp_paths->hash_mask + 1))
grow = 1;
spin_unlock(&mpp_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
if (grow) {
struct mesh_table *oldtbl, *newtbl;
write_lock(&pathtbl_resize_lock);
oldtbl = mpp_paths;
newtbl = mesh_table_grow(mpp_paths);
if (!newtbl) {
write_unlock(&pathtbl_resize_lock);
return 0;
}
rcu_assign_pointer(mpp_paths, newtbl);
write_unlock(&pathtbl_resize_lock);
synchronize_rcu();
mesh_table_free(oldtbl, false);
}
return 0;
err_exists:
spin_unlock(&mpp_paths->hashwlock[hash_idx]);
read_unlock(&pathtbl_resize_lock);
kfree(new_node);
err_node_alloc:
kfree(new_mpath);
err_path_alloc:
return err;
}
/**
* mesh_plink_broken - deactivates paths and sends perr when a link breaks
*
@ -475,11 +589,21 @@ static int mesh_path_node_copy(struct hlist_node *p, struct mesh_table *newtbl)
int mesh_pathtbl_init(void)
{
mesh_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
if (!mesh_paths)
return -ENOMEM;
mesh_paths->free_node = &mesh_path_node_free;
mesh_paths->copy_node = &mesh_path_node_copy;
mesh_paths->mean_chain_len = MEAN_CHAIN_LEN;
if (!mesh_paths)
mpp_paths = mesh_table_alloc(INIT_PATHS_SIZE_ORDER);
if (!mpp_paths) {
mesh_table_free(mesh_paths, true);
return -ENOMEM;
}
mpp_paths->free_node = &mesh_path_node_free;
mpp_paths->copy_node = &mesh_path_node_copy;
mpp_paths->mean_chain_len = MEAN_CHAIN_LEN;
return 0;
}
@ -511,4 +635,5 @@ void mesh_path_expire(struct ieee80211_sub_if_data *sdata)
void mesh_pathtbl_unregister(void)
{
mesh_table_free(mesh_paths, true);
mesh_table_free(mpp_paths, true);
}

View File

@ -1107,10 +1107,6 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
hdrlen = ieee80211_hdrlen(hdr->frame_control);
if (ieee80211_vif_is_mesh(&sdata->vif))
hdrlen += ieee80211_get_mesh_hdrlen(
(struct ieee80211s_hdr *) (skb->data + hdrlen));
/* convert IEEE 802.11 header + possible LLC headers into Ethernet
* header
* IEEE 802.11 address fields:
@ -1134,6 +1130,15 @@ ieee80211_data_to_8023(struct ieee80211_rx_data *rx)
if (unlikely(sdata->vif.type != NL80211_IFTYPE_WDS &&
sdata->vif.type != NL80211_IFTYPE_MESH_POINT))
return -1;
if (ieee80211_vif_is_mesh(&sdata->vif)) {
struct ieee80211s_hdr *meshdr = (struct ieee80211s_hdr *)
(skb->data + hdrlen);
hdrlen += ieee80211_get_mesh_hdrlen(meshdr);
if (meshdr->flags & MESH_FLAGS_AE_A5_A6) {
memcpy(dst, meshdr->eaddr1, ETH_ALEN);
memcpy(src, meshdr->eaddr2, ETH_ALEN);
}
}
break;
case __constant_cpu_to_le16(IEEE80211_FCTL_FROMDS):
if (sdata->vif.type != NL80211_IFTYPE_STATION ||
@ -1393,6 +1398,25 @@ ieee80211_rx_h_mesh_fwding(struct ieee80211_rx_data *rx)
/* illegal frame */
return RX_DROP_MONITOR;
if (mesh_hdr->flags & MESH_FLAGS_AE_A5_A6){
struct ieee80211_sub_if_data *sdata;
struct mesh_path *mppath;
sdata = IEEE80211_DEV_TO_SUB_IF(rx->dev);
rcu_read_lock();
mppath = mpp_path_lookup(mesh_hdr->eaddr2, sdata);
if (!mppath) {
mpp_path_add(mesh_hdr->eaddr2, hdr->addr4, sdata);
} else {
spin_lock_bh(&mppath->state_lock);
mppath->exp_time = jiffies;
if (compare_ether_addr(mppath->mpp, hdr->addr4) != 0)
memcpy(mppath->mpp, hdr->addr4, ETH_ALEN);
spin_unlock_bh(&mppath->state_lock);
}
rcu_read_unlock();
}
if (compare_ether_addr(rx->dev->dev_addr, hdr->addr3) == 0)
return RX_CONTINUE;

View File

@ -1498,18 +1498,50 @@ int ieee80211_subif_start_xmit(struct sk_buff *skb,
#ifdef CONFIG_MAC80211_MESH
case NL80211_IFTYPE_MESH_POINT:
fc |= cpu_to_le16(IEEE80211_FCTL_FROMDS | IEEE80211_FCTL_TODS);
/* RA TA DA SA */
memset(hdr.addr1, 0, ETH_ALEN);
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 (!sdata->u.mesh.mshcfg.dot11MeshTTL) {
/* Do not send frames with mesh_ttl == 0 */
sdata->u.mesh.mshstats.dropped_frames_ttl++;
ret = 0;
goto fail;
}
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
memset(&mesh_hdr, 0, sizeof(mesh_hdr));
if (compare_ether_addr(dev->dev_addr,
skb->data + ETH_ALEN) == 0) {
/* RA TA DA SA */
memset(hdr.addr1, 0, ETH_ALEN);
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
memcpy(hdr.addr3, skb->data, ETH_ALEN);
memcpy(hdr.addr4, skb->data + ETH_ALEN, ETH_ALEN);
meshhdrlen = ieee80211_new_mesh_header(&mesh_hdr, sdata);
} else {
/* packet from other interface */
struct mesh_path *mppath;
memset(hdr.addr1, 0, ETH_ALEN);
memcpy(hdr.addr2, dev->dev_addr, ETH_ALEN);
memcpy(hdr.addr4, dev->dev_addr, ETH_ALEN);
if (is_multicast_ether_addr(skb->data))
memcpy(hdr.addr3, skb->data, ETH_ALEN);
else {
rcu_read_lock();
mppath = mpp_path_lookup(skb->data, sdata);
if (mppath)
memcpy(hdr.addr3, mppath->mpp, ETH_ALEN);
else
memset(hdr.addr3, 0xff, ETH_ALEN);
rcu_read_unlock();
}
mesh_hdr.flags |= MESH_FLAGS_AE_A5_A6;
mesh_hdr.ttl = sdata->u.mesh.mshcfg.dot11MeshTTL;
put_unaligned(cpu_to_le32(sdata->u.mesh.mesh_seqnum), &mesh_hdr.seqnum);
memcpy(mesh_hdr.eaddr1, skb->data, ETH_ALEN);
memcpy(mesh_hdr.eaddr2, skb->data + ETH_ALEN, ETH_ALEN);
sdata->u.mesh.mesh_seqnum++;
meshhdrlen = 18;
}
hdrlen = 30;
break;
#endif