diff --git a/include/net/mac80211.h b/include/net/mac80211.h index 855754d4c50d..494a4c022a9b 100644 --- a/include/net/mac80211.h +++ b/include/net/mac80211.h @@ -442,16 +442,17 @@ struct ieee80211_conf { * @IEEE80211_IF_TYPE_IBSS: interface in IBSS (ad-hoc) mode. * @IEEE80211_IF_TYPE_MNTR: interface in monitor (rfmon) mode. * @IEEE80211_IF_TYPE_WDS: interface in WDS mode. - * @IEEE80211_IF_TYPE_VLAN: not used. + * @IEEE80211_IF_TYPE_VLAN: VLAN interface bound to an AP, drivers + * will never see this type. */ enum ieee80211_if_types { - IEEE80211_IF_TYPE_AP = 0x00000000, - IEEE80211_IF_TYPE_MGMT = 0x00000001, - IEEE80211_IF_TYPE_STA = 0x00000002, - IEEE80211_IF_TYPE_IBSS = 0x00000003, - IEEE80211_IF_TYPE_MNTR = 0x00000004, - IEEE80211_IF_TYPE_WDS = 0x5A580211, - IEEE80211_IF_TYPE_VLAN = 0x00080211, + IEEE80211_IF_TYPE_AP, + IEEE80211_IF_TYPE_MGMT, + IEEE80211_IF_TYPE_STA, + IEEE80211_IF_TYPE_IBSS, + IEEE80211_IF_TYPE_MNTR, + IEEE80211_IF_TYPE_WDS, + IEEE80211_IF_TYPE_VLAN, }; /** diff --git a/net/mac80211/debugfs_netdev.c b/net/mac80211/debugfs_netdev.c index c9948fa58e08..f0e6ab7eb624 100644 --- a/net/mac80211/debugfs_netdev.c +++ b/net/mac80211/debugfs_netdev.c @@ -162,9 +162,6 @@ __IEEE80211_IF_FILE(beacon_tail_len); /* WDS attributes */ IEEE80211_IF_FILE(peer, u.wds.remote_addr, MAC); -/* VLAN attributes */ -IEEE80211_IF_FILE(vlan_id, u.vlan.id, DEC); - #define DEBUGFS_ADD(name, type)\ sdata->debugfs.type.name = debugfs_create_file(#name, 0444,\ sdata->debugfsdir, sdata, &name##_ops); @@ -223,7 +220,6 @@ static void add_vlan_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_ADD(drop_unencrypted, vlan); DEBUGFS_ADD(eapol, vlan); DEBUGFS_ADD(ieee8021_x, vlan); - DEBUGFS_ADD(vlan_id, vlan); } static void add_monitor_files(struct ieee80211_sub_if_data *sdata) @@ -317,7 +313,6 @@ static void del_vlan_files(struct ieee80211_sub_if_data *sdata) DEBUGFS_DEL(drop_unencrypted, vlan); DEBUGFS_DEL(eapol, vlan); DEBUGFS_DEL(ieee8021_x, vlan); - DEBUGFS_DEL(vlan_id, vlan); } static void del_monitor_files(struct ieee80211_sub_if_data *sdata) diff --git a/net/mac80211/ieee80211.c b/net/mac80211/ieee80211.c index 319ec2a1d84f..4e345f82f044 100644 --- a/net/mac80211/ieee80211.c +++ b/net/mac80211/ieee80211.c @@ -314,22 +314,43 @@ static int ieee80211_open(struct net_device *dev) int res; sdata = IEEE80211_DEV_TO_SUB_IF(dev); + read_lock(&local->sub_if_lock); list_for_each_entry(nsdata, &local->sub_if_list, list) { struct net_device *ndev = nsdata->dev; if (ndev != dev && ndev != local->mdev && netif_running(ndev) && - compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0 && - !identical_mac_addr_allowed(sdata->type, nsdata->type)) { - read_unlock(&local->sub_if_lock); - return -ENOTUNIQ; + compare_ether_addr(dev->dev_addr, ndev->dev_addr) == 0) { + /* + * check whether it may have the same address + */ + if (!identical_mac_addr_allowed(sdata->type, + nsdata->type)) { + read_unlock(&local->sub_if_lock); + return -ENOTUNIQ; + } + + /* + * can only add VLANs to enabled APs + */ + if (sdata->type == IEEE80211_IF_TYPE_VLAN && + nsdata->type == IEEE80211_IF_TYPE_AP && + netif_running(nsdata->dev)) + sdata->u.vlan.ap = nsdata; } } read_unlock(&local->sub_if_lock); - if (sdata->type == IEEE80211_IF_TYPE_WDS && - is_zero_ether_addr(sdata->u.wds.remote_addr)) - return -ENOLINK; + switch (sdata->type) { + case IEEE80211_IF_TYPE_WDS: + if (is_zero_ether_addr(sdata->u.wds.remote_addr)) + return -ENOLINK; + break; + case IEEE80211_IF_TYPE_VLAN: + if (!sdata->u.vlan.ap) + return -ENOLINK; + break; + } if (local->open_count == 0) { res = 0; @@ -340,6 +361,10 @@ static int ieee80211_open(struct net_device *dev) } switch (sdata->type) { + case IEEE80211_IF_TYPE_VLAN: + list_add(&sdata->u.vlan.list, &sdata->u.vlan.ap->u.ap.vlans); + /* no need to tell driver */ + break; case IEEE80211_IF_TYPE_MNTR: /* must be before the call to ieee80211_configure_filter */ local->monitors++; @@ -407,9 +432,24 @@ static int ieee80211_stop(struct net_device *dev) dev_mc_unsync(local->mdev, dev); + /* down all dependent devices, that is VLANs */ + if (sdata->type == IEEE80211_IF_TYPE_AP) { + struct ieee80211_sub_if_data *vlan, *tmp; + + list_for_each_entry_safe(vlan, tmp, &sdata->u.ap.vlans, + u.vlan.list) + dev_close(vlan->dev); + WARN_ON(!list_empty(&sdata->u.ap.vlans)); + } + local->open_count--; switch (sdata->type) { + case IEEE80211_IF_TYPE_VLAN: + list_del(&sdata->u.vlan.list); + sdata->u.vlan.ap = NULL; + /* no need to tell driver */ + break; case IEEE80211_IF_TYPE_MNTR: local->monitors--; if (local->monitors == 0) { diff --git a/net/mac80211/ieee80211_i.h b/net/mac80211/ieee80211_i.h index 74deecd09677..1a43f3e9b6bd 100644 --- a/net/mac80211/ieee80211_i.h +++ b/net/mac80211/ieee80211_i.h @@ -191,6 +191,8 @@ struct ieee80211_if_ap { u8 *beacon_head, *beacon_tail; int beacon_head_len, beacon_tail_len; + struct list_head vlans; + u8 ssid[IEEE80211_MAX_SSID_LEN]; size_t ssid_len; u8 *generic_elem; @@ -214,7 +216,8 @@ struct ieee80211_if_wds { }; struct ieee80211_if_vlan { - u8 id; + struct ieee80211_sub_if_data *ap; + struct list_head list; }; /* flags used in struct ieee80211_if_sta.flags */ @@ -377,7 +380,6 @@ struct ieee80211_sub_if_data { struct dentry *drop_unencrypted; struct dentry *eapol; struct dentry *ieee8021_x; - struct dentry *vlan_id; } vlan; struct { struct dentry *mode; diff --git a/net/mac80211/ieee80211_iface.c b/net/mac80211/ieee80211_iface.c index f9c74bb09d31..4590205fdf4b 100644 --- a/net/mac80211/ieee80211_iface.c +++ b/net/mac80211/ieee80211_iface.c @@ -164,6 +164,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type) sdata->bss = NULL; break; case IEEE80211_IF_TYPE_VLAN: + sdata->u.vlan.ap = NULL; break; case IEEE80211_IF_TYPE_AP: sdata->u.ap.dtim_period = 2; @@ -171,6 +172,7 @@ void ieee80211_if_set_type(struct net_device *dev, int type) sdata->u.ap.max_ratectrl_rateidx = -1; skb_queue_head_init(&sdata->u.ap.ps_bc_buf); sdata->bss = &sdata->u.ap; + INIT_LIST_HEAD(&sdata->u.ap.vlans); break; case IEEE80211_IF_TYPE_STA: case IEEE80211_IF_TYPE_IBSS: { @@ -284,6 +286,9 @@ void ieee80211_if_reinit(struct net_device *dev) case IEEE80211_IF_TYPE_MNTR: dev->type = ARPHRD_ETHER; break; + case IEEE80211_IF_TYPE_VLAN: + sdata->u.vlan.ap = NULL; + break; } /* remove all STAs that are bound to this virtual interface */